Solari
Solari

Reputation: 53

How to call another HTTP request after forkJoin is completed (RxJS)?

I have looked this for quite awhile, but I really haven't figured out how to do it "properly". All the other answers are either deprecated or do this in the opposite order.

What I would like to do, is that I want to send a bunch of HTTP request in parallel, thus the forkJoin. Then and only then when they are completed, I would like to call another HTTP request (which is now inside the submitOrder()).

And then at some point, if the first HTTP requests were successful I would like to mark my objects not dirty again.

This code works, but I've understood that this is really not the "proper" way of doing it, because it call subscription inside of a subscription.

So please, what is the real RxJs way?

forkJoin(obsArray).subscribe(res => {
            console.log('All were resolved');
            this.products.forEach(element => {
                this.isNotDirtyAnymore();
            });
            this.submitOrder();
        });


submitOrder() {
        this.service.submitOrder(this.id).subscribe(response => {
            console.log('Order was submitted');
        }, error => {
            console.log(error);
        });
    }

Upvotes: 2

Views: 2809

Answers (4)

martin
martin

Reputation: 96891

If you don't need to results from the forkJoin you could do it like this:

concat(
  forkJoin(obsArray),
  this.service.submitOrder(this.id)
).subscribe({
  complete: () => { // This will be triggered only when `concat` completes which means both source Observables complete (won't emit error).
    this.products.forEach(element => {
      this.isNotDirtyAnymore();
    });
  },
})

Make sure you import import { concat } from 'rxjs'; and not from rxjs/operators.

Upvotes: 2

Thibs
Thibs

Reputation: 8258

Here is an example of my comment above (not tested):

   forkJoin([
        this.service.callItem1,
        this.service.callItem2
    ]).pipe(
        tap(([resp1, resp2]: [any, any])=> {
            this.products.forEach(element => {
                this.isNotDirtyAnymore();
            });
        }),
        switchMap(([resp1, resp2]: [any, any]) => this.service.submitOrder(this.id))
    ).subscribe(response => {
        // if you don't need to do this in the tap above you can do it here
        this.products.forEach(element => {
            this.isNotDirtyAnymore();
        });
        console.log('Order was submitted');
    }, error => {
        console.log(error);
    });

Upvotes: 3

Federico Andreoli
Federico Andreoli

Reputation: 465

You can use the pipe operator.

Example from http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html:

import { map, filter, scan } from 'rxjs/operators';

Rx.Observable.interval(1000)
  .pipe(
     filter(x => x % 2 === 0),
     map(x => x + x),
     scan((acc, x) => acc + x)
   ).subscribe(x => console.log(x))

Upvotes: 0

shenghua lian
shenghua lian

Reputation: 149

As forkJoin actually return a new observable, you can just treat is as a normal observable. Then a normal hight order operator could be used, switchMap, contactMap... And put the side effect into tap. For example:

forkJoin(obsArray).pipe(
  tap(() => {
    console.log('All were resolved');
    this.products.forEach(element => this.isNotDirtyAnymore()),
  }),
  switchMap(() => submitOrder()),
});

Upvotes: 2

Related Questions