How to make a sequence of http requests in Angular 6 using RxJS
Asked Answered
T

6

22

I've been looking for a solution all over the web, but couldn't find anything that fits my user case. I'm using the MEAN stack (Angular 6) and I have a registration form. I'm looking for a way to execute multiple HTTP calls to the API and each one is dependent on the returned result from the previous one. I need something that looks like this:

firstPOSTCallToAPI('url', data).pipe(
    result1 => secondPOSTCallToAPI('url', result1)
    result2 => thirdPOSTCallToAPI('url', result2)
    result3 => fourthPOSTCallToAPI('url', result3)
    ....
).subscribe(
    success => { /* display success msg */ },
    errorData => { /* display error msg */ }
);

What combination of RxJS operators do I need to use to achieve this? One possible solution would be to nest multiple subscriptions, but I want to avoid that and do it better with RxJS. Also need to think about error handling.

Talia answered 30/11, 2018 at 15:43 Comment(3)
Is your number of observables defined ? Or does the code has to be dynamic ?Lungworm
you can use flatMap or switchMapBluebird
rguerin, I would like to be flexible and not depend on exact number of observables. @Bluebird I tried using switchMap but i get an error saying "Expected 1-2 arguments, but got 3". Can you give an example, please?Talia
F
63

For calls that depend on previous result you should use concatMap

firstPOSTCallToAPI('url', data).pipe(
    concatMap(result1 => secondPOSTCallToAPI('url', result1))
      concatMap( result2 => thirdPOSTCallToAPI('url', result2))
       concatMap(result3 => fourthPOSTCallToAPI('url', result3))
    ....
).subscribe(
    success => { /* display success msg */ },
    errorData => { /* display error msg */ }
);

if your async method does not depend on return value of the precedent async call you can use

   concat(method(),method2(),method3()).subscribe(console.log)
Fetial answered 30/11, 2018 at 16:4 Comment(6)
Exactly what I was looking for!!! Thank you very much, you have ended my suffering!Talia
You beautiful human!! Got me out of a bind here. Thanks!Costrel
I found this very helpful. blog.angular-university.io/rxjs-higher-order-mappingWaldheim
@Drenai yep switchMap and mergeMap is also applicable here. Since http is one off emissionFetial
How would i get access to result1 or result2 in the success part at the endSouse
you can pass down stream with map : concatMap(result1 => secondPOSTCallToAPI('url', result1).pipe(map(result2=>[result1,result2])Fetial
O
2

I have faced same problem, this is my solution use pipe and concatMap for array for get sequence data for time period between start and end time.

This is general solution when we have array request.

I share for whom concern.

 let currentReplayData = [];
 let timerange = [[t1, t2], [t3, t4]]; // array of start and end time
 from(timerange).pipe(
      concatMap(time => <Observable<any>>this.dataService.getData(time[0],time[1]))
      ).subscribe(val => {
        //console.log(val)
        this.currentReplayData = this.currentReplayData.concat(val);
      });
Obverse answered 4/3, 2021 at 9:49 Comment(0)
N
1

MergeMap

is exact what you are looking for

firstPOSTCallToAPI('url', data).pipe(
    mergeMap(result1 => secondPOSTCallToAPI('url', result1)),
    mergeMap(result2 => thirdPOSTCallToAPI('url', result2)),
    mergeMap(result3 => fourthPOSTCallToAPI('url', result3)),
    // ...
).subscribe(
    success => { 
      // here you will get response of LAST request (fourthPOSTCallToAPI)
    },
    errorData => { /* display error msg */ }
);


// I assume that
// secondPOSTCallToAPI, thirdPOSTCallToAPI and fourthPOSTCallToAPI
// returns obserwable eg. return this.http.get<Book>(apiUrl);
Niggle answered 17/1, 2022 at 14:56 Comment(0)
E
0
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/forkJoin';

@Injectable()
export class DataService {

  constructor(private http: HttpClient) { }

  public requestDataFromMultipleSources(): Observable<any[]> {
    let response1 = this.http.get(requestUrl1);
    let response2 = this.http.get(requestUrl2);
    let response3 = this.http.get(requestUrl3);
    return Observable.forkJoin([response1, response2, response3]);
  }
}

The above example shows making three http calls, but in a similar way you can request as many http calls as required

    import { Component, OnInit } from '@angular/core';
import { DataService } from "../data.service";

@Component({
    selector: 'app-page',
    templateUrl: './page.component.html',
    styleUrls: ['./page.component.css']
})
export class DemoComponent implements OnInit {
    public responseData1: any;
    public responseData2: any;
    public responseData3: any;

    constructor(private dataService: DataService) {}

    ngOnInit() {
        this.dataService.requestDataFromMultipleSources().subscribe(responseList => {
            this.responseData1 = responseList[0];
            this.responseData2 = responseList[1];
            this.responseData3 = responseList[1];
        });
    }
}
Elysian answered 30/11, 2018 at 16:1 Comment(0)
M
0

Let me show you how to here, assuming I have much of emails I want to deliver an email to sequentially:

sendEmails() {
  this.isLoading = true; 
            const calls = this.emails <<-- assume this contain an array of emails
            .map(email => this.userEmailService.deliver({email: email, userId: 1242}));
            from(calls) <<-- make use of the from.
                .pipe(
                    concatMap(res => res),
                    finalize(() => this.isLoading = false)
                ).subscribe(() => { });
}

I hope this helps.

Maros answered 8/5, 2019 at 9:56 Comment(0)
V
-4

Try this , Angular provides feature to call multiple API at a time.

forkJoin()

You will get data in array as in same sequence which you call API.

Ex:

forkJoin(request1, request2)
    .subscribe(([response1, response2]) => {

You can find more read

I have also given another answer. Please check this, it may also helps you.

Vi answered 30/11, 2018 at 15:51 Comment(3)
forkJoin sends all at once AFAIK, using concat ensures the previous one completesComptometer
Thanks for the reply. I've looked into forkJoin but it doesn't look like a working solution since i need request2 to use response data returned from request1.Talia
In that case , forkJoin will not use. I suggest to check second link. It will work in you case.Vi

© 2022 - 2024 — McMap. All rights reserved.