如何使用 RxJS 在 Angular 6 中发出一系列 http 请求

Posted

技术标签:

【中文标题】如何使用 RxJS 在 Angular 6 中发出一系列 http 请求【英文标题】:How to make a sequence of http requests in Angular 6 using RxJS 【发布时间】:2019-05-02 19:02:13 【问题描述】:

我一直在网上寻找解决方案,但找不到任何适合我的用户案例的解决方案。我正在使用 MEAN 堆栈(Angular 6)并且我有一个注册表单。我正在寻找一种方法来执行对 API 的多个 HTTP 调用,每个调用都依赖于前一个返回的结果。我需要看起来像这样的东西:

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 做得更好。还需要考虑错误处理。

【问题讨论】:

是否定义了可观察的数量?还是代码必须是动态的? 你可以使用 flatMap 或 switchMap rguerin,我想灵活一点,不依赖于可观察对象的确切数量。 @JEY我尝试使用switchMap,但我收到一条错误消息“预期1-2个参数,但得到3个”。可以举个例子吗? 【参考方案1】:

对于依赖于先前结果的调用,您应该使用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)

【讨论】:

正是我想要的!!!非常感谢,你结束了我的痛苦! 在上述情况下, concatMap 没有按照您的想法进行操作。您可以在这里轻松使用 mergeMap @FanCheung 它可以正常工作,但如果您将示例中的concatMap 替换为mergeMapswitchMap,它仍然可以工作。 concatMapwait until complete 方面仅在您通过管道传输多个数据的 observable 时才明显,而 firstPOSTCallToAPI 仅传输单个事件,第二个、第三个和第四个调用也是如此 你这个美丽的人类!!让我摆脱了这里的束缚。谢谢!【参考方案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);
      );

【讨论】:

【参考方案3】:

MergeMap

正是你要找的东西

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

【讨论】:

【参考方案4】:
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];
        );
    

【讨论】:

【参考方案5】:

让我在这里向您展示如何操作,假设我有很多要按顺序发送电子邮件的电子邮件:

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

我希望这会有所帮助。

【讨论】:

【参考方案6】:

试试这个,Angular 提供了一次调用多个 API 的功能。

forkJoin()

您将在array 中以与调用 API 相同的顺序获取数据。

例如:

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

你可以找到更多read

我还给了另一个answer。请检查一下,它也可能对您有所帮助。

【讨论】:

forkJoin 一次发送所有 AFAIK,使用 concat 确保前一个完成 感谢您的回复。我已经研究了 forkJoin,但它看起来不像一个可行的解决方案,因为我需要 request2 来使用从 request1 返回的响应数据。 这种情况下,forkJoin 不会使用。我建议检查第二个链接。它适用于你的情况。

以上是关于如何使用 RxJS 在 Angular 6 中发出一系列 http 请求的主要内容,如果未能解决你的问题,请参考以下文章

Angular:如何使用 RXJS 6 调用 finally()

Angular/RxJS 6:如何防止重复的 HTTP 请求?

Angular 6 RXJS 导入语法?

无法在 RxJs 6 和 Angular 6 中使用 Observable.of

如何每隔一段时间发出 HTTP 请求?

RxJS之过滤操作符 ( Angular环境 )