Home > Mobile >  Angular 12: Wait for fetching all data (forkjoin) before proceeding
Angular 12: Wait for fetching all data (forkjoin) before proceeding

Time:09-22

In my ngOnInit, I would like to wait for all requests of fetchLists to finish before proceeding:

  ngOnInit(): void {
    this.fetchLists();

    this.route.params.subscribe(params => {
         this.doSomethingWithFetchedLists();
      }
    });
  }

  fetchLists(): void {
    this.httpHandlerCached.getListsA()
      .subscribe(listA => this.listA = listA);
    this.httpHandlerCached.getListsB()
      .subscribe(listB => this.listB = listB);
    this.httpHandlerCached.getListsC()
      .subscribe(listC => this.listC = listC);
  }

Please hear me out, my previous question was closed with a reference to use "forkJoin": Wait for multiple promises to finish

However, with forkjoin I have the exact same issue:

  fetchListnames() {
    return forkJoin([
      this.httpHandlerCached.getListsA(),
      this.httpHandlerCached.getListsB(),
      this.httpHandlerCached.getListsC(),
    ]).subscribe(res => {
        this.listA = res[0];
        this.listB = res[1];
        this.listC = res[2];
      });
  }

So with the proposal to use forkJoin, how can I now wait for the forkjoin to finish before proceeding (meaning before this.doSomethingWithFetchedLists() is called)?

CodePudding user response:

it's not recommended to put subscriptions inside of subscriptions as suggested by another answer. Instead, one should rely on rxjs pipes and subscribe just once.

In this case, one could think that putting this.route.params inside the forkJoin would do the trick, but because this.route.params will never complete, forkJoin won't emit (that's how forkJoin is implemented). To make this.route.params complete, you can pipe it into take(1), resulting in this code:

forkJoin([
      this.httpHandlerCached.getListsA(),
      this.httpHandlerCached.getListsB(),
      this.httpHandlerCached.getListsC(),
      this.route.params.pipe(take(1))
    ]).subscribe(res => {
        this.listA = res[0];
        this.listB = res[1];
        this.listC = res[2];
        this.doSomethingWithFetchedLists();
      });

You can also use combineLatest instead of forkJoin, which instead of waiting for all observables to complete, will emit everytime one of the observables changes (but it will wait for all observables to emit at least one value first).

combineLatest([
          this.httpHandlerCached.getListsA(),
          this.httpHandlerCached.getListsB(),
          this.httpHandlerCached.getListsC(),
          this.route.params
        ]).subscribe(res => {
            this.listA = res[0];
            this.listB = res[1];
            this.listC = res[2];
            this.doSomethingWithFetchedLists();
          });

If you use the latter approach, you have to unsubscribe manually (or pipe to take(1)). Otherwise you will have memory leaks.

CodePudding user response:

Adding to Christian's answer, you can use withLatestFrom to avoid nested subscriptions

fetchListnames() {
    return forkJoin([
        this.httpHandlerCached.getListsA(),
        this.httpHandlerCached.getListsB(),
        this.httpHandlerCached.getListsC(),
    ])
    .pipe(
        withLatestFrom(this.route.params.pipe(take1)),
        map(([res, routeParams]) => {
            return { res, routeParams }    
        })
    )
    .subscribe(response => {
        let res = response.res;
        let routeParams = response.routeParams;
        this.listA = res[0];
        this.listB = res[1];
        this.listC = res[2];
        this.doSomethingWithFetchedLists();
        });
}
  • Related