当最后一个可观察到的值发射完毕时,如何获取通知?

时间:2019-10-09 18:46:13

标签: angular rxjs

我们曾经只调用过一次API

reqFinanceDataWithFilters(req): Observable<any> {
  return this.http.post(env.baseUrl + req.url, req.filters)
    .pipe(map(this.extractResults));
}

以上请求将返回一个巨型json,需要花费一些时间在API级别进行构建。因此,我们决定将其分为4个通话。现在我们有:

reqViz.url = 'viz-data';
this.reqFinanceDataWithFilters(reqViz)
    .pipe(...)

reqTableTop.url = 'summary-data';
this.reqFinanceDataWithFilters(reqTableTop)
    .pipe(...)

reqTableMiddle.url = 'monthly-expenses';
this.reqFinanceDataWithFilters(reqTableMiddle)
    .pipe(...)

所以我有4个独立的API调用。每个响应的顺序并不重要。我想知道的最后一个完成接收响应的时间,以便隐藏微调框并显示页面。

感谢您的帮助

3 个答案:

答案 0 :(得分:5)

forkJoin(/* your observalbes here */).subscribe(values => {
   // do stuff, all observables have completed
})

https://www.learnrxjs.io/operators/combination/forkjoin.html

答案 1 :(得分:5)

您可以为此使用forkJoin

来自 LearnRxJS.io

  

为什么要使用forkJoin?

     

当您有一组可观察对象并且仅关心每个可观察对象的最终发射值时,最好使用此运算符。一个常见的用例是,如果您希望在页面加载(或其他事件)时发出多个请求,并且只希望在收到所有人的响应后才采取措施。这样就类似于您使用Promise.all的方式。

     

请注意,如果提供给forkJoin错误的任何内部可观察变量,您将丢失如果无法正确捕获内部可观察变量错误而将已经或已经完成的任何其他可观察变量的值。如果您只关心所有内部观测值是否成功完成,则可以从外部捕获错误。

在这里,尝试一下:

import { forkJoin } from 'rxjs';
import { delay, catchError } from 'rxjs/operators';

...

loading = true;
reqViz.url = 'viz-data';
const viz = this.reqFinanceDataWithFilters(reqViz)
  .pipe(
    ...
    catchError(error => of(error))
  )

reqTableTop.url = 'summary-data';
const summary = this.reqFinanceDataWithFilters(reqTableTop)
  .pipe(
    ...
    catchError(error => of(error))
  )

reqTableMiddle.url = 'monthly-expenses';
const monthy = this.reqFinanceDataWithFilters(reqTableMiddle)
  .pipe(
    ...
    catchError(error => of(error))
  )

forkJoin(viz, summary, monthly)
  .subscribe(
    res => {
      loading = false;
    }
  )

答案 2 :(得分:3)

forkJoinmerge可能是有趣的选择,但另一种方法是通过from使用concatMap进行api调用,将源转换为流。最后调用finalize

哪个更好?好吧,这确实是一个品味问题。来自管道的流一次将包含一个结果,而不是像forkJoin那样包含所有对象的数组。如果您需要在api调用之后在每个项目上运行类似的进程,则可以使用此方法。例如,仅当您想在UI中显示进度时,才可以使用此方法。

isRunning = true;
completedItems = 0;
progress = 0;
from([reqViz, reqTableTop, reqTableMiddle]).pipe(
   concatMap(req => this.reqFinanceDataWithFilters(reqTableMiddle)),
   tap(() => progress = (++completedItems) / 3),
   finalize(() => isRunning = false) // this is called at the end no matter what.
);