如何在Angular 6中使用RxJS进行一系列的HTTP请求

19

我在网上寻找解决方案,但没有找到适合我的用户情况的。我正在使用MEAN堆栈(Angular 6),并且有一个注册表单。我需要一种方法来执行多个HTTP调用API,并且每个调用取决于前一个返回的结果。我需要类似于这样的东西:

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 */ }
);

我需要使用哪些RxJS操作符来实现这个功能?一种可能的解决方案是嵌套多个订阅,但我想避免这样做,而是更好地使用RxJS。同时还需要考虑错误处理。


你的可观测数量是否已定义?还是代码必须是动态的? - rguerin
你可以使用 flatMap 或 switchMap。 - JEY
rguerin,我想要灵活一些,不要依赖于确切的可观察数量。@JEY 我尝试使用switchMap,但是出现了一个错误,说“期望1-2个参数,但得到了3个”。你能给个例子吗? - Andi Aleksandrov
6个回答

60

对于依赖于前一个结果的调用,您应该使用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 */ }
);

如果您的异步方法不依赖于前置异步调用的返回值,您可以使用

   concat(method(),method2(),method3()).subscribe(console.log)

2
正是我所需要的!!!非常感谢,你结束了我的痛苦! - Andi Aleksandrov
在上述情况下,concatMap 没有做你认为它应该做的事情。你同样可以在这里使用 mergeMap。 - Drenai
你真是个了不起的人啊!!帮我解决了难题。谢谢! - Alan Smith
我觉得这篇文章非常有用。https://blog.angular-university.io/rxjs-higher-order-mapping/ - macawm
你可以使用 map 将下游传递: concatMap(result1 => secondPOSTCallToAPI('url', result1).pipe(map(result2=>[result1,result2]) - Fan Cheung
显示剩余2条评论

2

我曾经遇到同样的问题,这是我的解决方案:使用pipeconcatMap来获取开始时间和结束时间之间的时间段内的序列数据。

对于我们有请求数组的一般情况,这是一个通用的解决方案。

我分享给关注此问题的任何人。

 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);
      });

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);


0

让我向您展示如何在编程中实现这一点,假设我有很多电子邮件要按顺序发送:

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(() => { });
}

希望这对你有所帮助。


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];
        });
    }
}

-4
尝试一下,Angular 提供了同时调用多个 API 的功能。
forkJoin()

您将会按照调用API时的相同顺序,以数组的形式获取数据。

例如:

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

你可以找到更多閱讀
我還提供了另一個答案。請檢查一下,它可能也會對您有所幫助。

9
据我所知,forkJoin 会一次性发送所有内容,使用 concat 可以确保前一个完整执行后再发送下一个。 - mchl18
谢谢回复。我已经研究了forkJoin,但它似乎不是一个可行的解决方案,因为我需要request2使用从request1返回的响应数据。 - Andi Aleksandrov
在这种情况下,不会使用forkJoin。我建议检查第二个链接。它将适用于您的情况。 - Sachin Shah

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接