RxJS:如何激发2个并行请求,其中一个是可选的,并且超时

时间:2019-05-09 11:55:50

标签: angular rxjs rxjs6

我有一种情况,我必须同时触发2个GET请求:

  1. 其中第一个是强制性。我必须等待响应,并且如果弹出一些错误,我将进行异常处理,并且我也可以取消我的2.请求。
  2. 第二个是可选。如果出现错误,我可以忽略这种情况。我想等待最大。 5秒“更多”,如果需要更长的时间,我想取消该请求(我知道我无法取消触发的请求,而只是忽略返回的值/或返回的错误)。因此,如果1.通话可能需要20秒。 2.通话最多可以等待。 25秒如果1.通话仅需要1秒。 2.呼叫等待时间不能超过6秒。等

我如何用rxjs实现呢?

我知道我可以压缩多个请求,但是到目前为止我看到的所有示例都只有1个错误处理块,但是在这里我需要区分两种错误情况。

预先感谢

2 个答案:

答案 0 :(得分:1)

除了解决方案,我还有更多解决方法。 您的要求是触发并行请求,并根据第一个请求的响应取消第二个。

可以使用forkJoin进行并行请求,但是所有可观察对象可以一起解决,

merge()也将触发并行请求,但是任何响应都可以按任何顺序进行。使用merge(),我们将无法确定哪个响应来自哪个Observable。如果您可以自由修改返回的observable并添加一个标志以指示有关Observable索引,则可以使用一些额外的标志和代码来实现它:

export class AppComponent  {
  name = 'Angular';
  obsOne = of('First Obs').pipe(map((res) => {
    return {
      firstObs: true,
      result: res
    }
  }))
  obsTwo = of('Second Obs').pipe(delay(6000))

  secondObsReturned = false
  timerHandle
  obsSubcription: Subscription;

  ngOnInit() {
    this.obsSubcription = merge(this.obsOne, this.obsTwo).subscribe((data) => {

      // you can add all this logic in pipe(map()) instead of handling in subscribe

      console.log(`data returned`, data)
      // some appropriate checks here
      if (typeof data === 'object' && data.hasOwnProperty('firstObs')) {
        if (!this.secondObsReturned) {
          // can use rxjs timer here
        this.timerHandle = setTimeout(() => {
          console.log('Delayed more than 5 seconds');
          this.obsSubcription.unsubscribe();
        }, 5000)
        }
      }
      else {
        // this is the second onservable (which may have come early)
        this.secondObsReturned = true;
      }
    })
  }
}

在此处查看示例:https://stackblitz.com/edit/angular-s6wkk2


编辑

因此,我在考虑某种避免更改返回的Observable的方法,于是我想到了CombineLatest。结合最新的功能是,它第一次会等待两个Observable中的值,此后,即使有任何Observable解析,它也会发出。

要使用此功能,再次有一个约束。例如,您需要知道Observables永远不会返回的特定值,例如false,因此,如果您知道Observables永远不会返回false(或任何默认值),则可以使用BehaviorSubjects和CombineLatest。使用永远无法返回的值初始化BehaviorSubjects。

您需要点按可观察对象以为对象添加值。

// give appropriate types
subjectOne = <any> new BehaviorSubject(false); // will contain value of the first observable
subjectTwo = <any> new BehaviorSubject(false); // will contain value of the second observable
takeUntilSub = new Subject(); // use this to stop the subscriptions

obsOne = of('First Obs')
  .pipe(
    tap((value) => {
      this.subjectOne.next(value);
    }),
    catchError((e) => {
      // if an Error occurs in first then you don't want to proceeed at all
      // add an error in the subjectOne, this will stop the combineLatest stream.
      this.subjectOne.error('Observable one errored')
      return throwError;(e)
    })
  )

obsTwo = of('Second Obs')
  .pipe(
    delay(6000),
    tap((value) => {
      this.subjectTwo.next(value);
    }),
    catchError((e) => {
      // if you want to continue the stream, you need to handle the error and return a success.
      // no need to populate the subject coz you don't care about this error
      return of(e)
    })
  )

secondObsReturned = false
timerHandle;

ngOnInit() {

  // calling the actual Observables here.
  merge(this.obsOne, this.obsTwo).pipe(takeUntil(this.takeUntilSub)).subscribe()

  // this will be called once for the very first time giving values as false for both of them (or the emitted initial values)
  // after that when any one of them resolves, flow will come here
  combineLatest(this.subjectOne, this.subjectTwo).pipe(takeUntil(this.takeUntilSub)).subscribe(([dataFromObsOne, dataFromObsTwo]) => {

    console.log(`data received: ${dataFromObsOne} and ${dataFromObsTwo}`)

    if (dataFromObsTwo !== false) {
      // second observable was resolved
      this.secondObsReturned = true;
      if (this.timerHandle) {
        clearTimeout(this.timerHandle);
      }
    }

    if (dataFromObsOne !== false) {
      // first observable resoved
      if (!this.secondObsReturned) {
        // if second obs hasn't already been resolved then start a timer.
        this.timerHandle = setTimeout(() => {
          console.log('Delayed more than 5 seconds');
          this.takeUntilSub.next(true);   // stop all subscriptions
        }, 5000)
      }
    }
  })
}

在此处查看示例:Code Link

答案 1 :(得分:-1)

您可以使用forkJoin运算符。当您有多个请求但必须等待第一个请求的响应然后可以触发下一个请求时,将使用此运算符。

有关更多信息,请参见forkJoin运算符的RxJS文档。

以下是我尝试汇总的代码段:


constructor(private http: HttpClient)

this.url1 = 'Url 1';
this.url2 = 'Url 2';

public forkJoinExample(): Observable<any> {

   let data_1 = this.http.get(this.url1);
   let data_2 = this.http.get(this.url2);

return forkJoin([data1, data2]);

}

要添加等待,可以使用defer运算符。实施defer也应该相当简单。

另一种实现方式可以是以下代码段:


   let data_1 = this.http.get(this.url1);
   let data_2 = this.http.get(this.url2);

const observableData = Rx.Observable.forkJoin([data1, data2]);

observableData.subscribe(
res => {
   // Handle response
},
(err) => {
   // Handle Error Scenario
},
() => {
   // Executes when all the requests are completed
});

还有另一种处理多个错误块的方法,如果您要添加,我可以添加这些错误块。您可以只分叉单个服务调用,最后订阅最终请求,并在这些单个请求中添加尽可能多的响应和错误块。