【问题标题】:How to make a sequence of http requests in Angular 6 using RxJS如何使用 RxJS 在 Angular 6 中发出一系列 http 请求
【发布时间】: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个”。可以举个例子吗?

标签: angular typescript rxjs angular-httpclient rxjs6


【解决方案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 不会使用。我建议检查第二个链接。它适用于你的情况。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-11-24
          • 2019-01-08
          • 1970-01-01
          • 2018-12-12
          相关资源
          最近更新 更多