0

I have a pb with my for each loop.

i want to iterate on my user list and for each, i i a api call to do to get some others stats informations. but the loop go to next before the call response of the first element.

I undestand that the for each is synchronous and not the subscribe... so How can i do this ?

here is my code source :

 this.users.forEach(userid => {
    this.httpClient.get(url).subscribe((res: DayStat[]) => {
      res.forEach(y => {
          // do some stuff
          console.log('log1');
      }
    });

    // do some others stuffs
    console.log('log2');
  });

  console.log ('end loop');
  // do some others stuffs;

log order :

end loop;
log1
log2

thanks for your help !

Eliseo
  • 50,109
  • 4
  • 29
  • 67
Jeff
  • 59
  • 2
  • 10

4 Answers4

1

Jeff, the http.get is asynchronous, so you don't know when it end.

When we want has a series of httpClient, usually you use forkJoin. Inside forkJoin you has all the responses, e.g.

const obs=[];
this.users.forEach(userid => {
    obs.push(this.httpClient.get(url))
}
//In obs we has an array of observables. when subscribe you get an array with the responses, e.g.

forkJoin(obs).susbcribe((result:any[])=>{
   result.map((res,index)=>{
      console.log(this.users[index], res)
   })
})
Eliseo
  • 50,109
  • 4
  • 29
  • 67
  • 1
    Instead of pushing the observable into an array, I would simply map the users into observables, like this: `const obs = this.users.map(userid => this.httpClient.get(url))`. Otherwise good answer. – ShamPooSham May 05 '20 at 12:30
  • Really, it's a better aproach – Eliseo May 05 '20 at 13:03
0

Something like this?

const requests$: Observable<DayStat[]>[] = users.forEach(user => getUserById(user.id));
zip(...requests$).subscribe();
Alexander
  • 3,019
  • 1
  • 20
  • 24
0

You can use "await" as mentioned already here: https://stackoverflow.com/a/48697112/1143214

C. Gäking
  • 163
  • 1
  • 9
  • It's not gonna work, because forEach is not async function so await is not allowed. It could be allowed, if you add `async` in front of `userid` like this `async userid =>`, but still whole expression is asynchronous and `console.log('end loop');` could be executed first – Damian Plewa May 05 '20 at 12:31
0

It's happening because http request is asynchronous, this solution should help

async method() {
  for (const userid of this.users) {
    const res: DayStat[] = await this.httpClient.get(url).toPromise();
    res.forEach(y => {
      // do some stuff
      console.log('log1');
    });
    console.log('log2');
  }

  console.log ('end loop');
  // do some others stuffs;
}
Damian Plewa
  • 1,123
  • 7
  • 13
  • wow ! thanks all for your help ! Thanks Damian, your solution is working for me ! just adding the type of result here : get. – Jeff May 05 '20 at 13:19
  • Oh ye, sorry for that. I wrote this code on blind, but that's good you reached your goal despite this :) – Damian Plewa May 05 '20 at 13:21
  • I'm not fond of converting observables to promises, it loses the strength of observables. – ShamPooSham May 05 '20 at 13:35
  • In this case, it doesn't matter that it's observable or not, because it's completed just right after response, it's not gonna emit any new values, so it's behaves like promise – Damian Plewa May 05 '20 at 13:40