Angular等待所有订阅完成

Posted

技术标签:

【中文标题】Angular等待所有订阅完成【英文标题】:Angular wait for all subscriptions to complete 【发布时间】:2021-04-14 01:26:32 【问题描述】:

在 Angular 中,一个页面对多个操作进行多次 http 调用,比如说按钮点击。但是,当最后一个“完成”按钮被按下时,我想确保所有这些请求都在它进行之前完成。我尝试将forkJoin 与可观察对象一起使用,但它本身会触发请求,这不是我想要做的,我希望其他操作来触发请求,并确保在单击“完成”时完成异步请求。有了承诺,我只需将承诺推送到数组,然后执行Promise.all(allRequests).then(()=>)

observables: Observable<any>[];

onBtn1Click()
   let o1 = this.service.doAction1();
   this.observables.push(o1);
   
   o1.subscribe(resp => 
        //do action1
   );


onBtn2Click()
   let o2 = this.service.doAction2();
   this.observables.push(o2);
   
   o2.subscribe(resp => 
        //do action2
   );


onDoneClick()
   // I would like something like this just that it wouldn't trigger the requests but make sure they are completed. 
   forkJoin(this.observables).subscribe(()=>
     //Proceed with other things
   );

【问题讨论】:

看看Rxjs中的finalize()操作符 【参考方案1】:

除非有人想出一个优雅的方法,否则应该这样做。

我正在创建一个对象来为来自 HTTP 请求的每个冷 observable 保存 hot observable。该请求将使用 RxJS finalize 运算符向其相应的热可观察对象发出。然后可以使用 forkJointake(1) 组合这些热门的 observable,以等待源请求完成。

private httpReqs:  [key: string]: ReplaySubject<boolean>  = Object.create(null);

onBtn1Click() 
  this.httpReqs['btn1'] = new ReplaySubject<boolean>(1);
  this.service.doAction1().pipe(
    finalize(() => this.httpReqs['btn1'].next(true))
  ).subscribe(resp => 
    // do action1
  );


onBtn2Click() 
  this.httpReqs['btn2'] = new ReplaySubject<boolean>(1);
  this.service.doAction1().pipe(
    finalize(() => this.httpReqs['btn2'].next(true))
  ).subscribe(resp => 
    // do action2
  );


onDoneClick()
  forkJoin(
    Object.values(this.httpReqs).map(repSub =>
      repSub.asObservable().pipe(
        take(1)
      )
    )
  ).subscribe(() => 
    // Proceed with other things
  );

【讨论】:

感谢工作正常。我只是使用let rs = new ReplaySubject&lt;boolean&gt;(1); httpReqs.push(rs); rs.next(true) 而不是分配给命名属性。【参考方案2】:

使用shareReplay

如果您进行多播,任何订阅已完成流的订阅者都会收到complete 通知。你可以利用它。

各种共享运算符都有一个隐含的refCount,每隔几个 RxJS 版本就会更改其默认值。 shareReplay(n) 的当前版本非常直观,但您可能需要在旧版本上设置 refCount:false,甚至使用 multicast(new ReplaySubject(1)), refCount()

onBtn1Click()
  let o1 = this.service.doAction1().pipe(
    shareReplay(1)
  );
  this.observables.push(o1);
   
  o1.subscribe(resp => 
    //do action1
  );

这是让您的代码按照您想要的方式运行的最小更改

扫描计数活动

如果只计算当前活动的操作,则可以完全避免 forkJoin。

count = (() => 
  const cc = new BehaviorSubject<number>(0);
  return 
    start: () => cc.next(1),
    stop: () => cc.next(-1),
    value$: cc.pipe(
      scan((acc, curr) => acc + curr, 0)
    )
  
)();

onBtn1Click()
  this.count.start();

  this.service.doAction1().pipe(
    finalize(this.count.stop)
  ).subscribe(resp => 
    //do action1
  );


onDoneClick()
  this.count.value$.pipe(
    first(v => v === 0) // Wait until nothing is currently active
  ).subscribe(() => 
    //Proceed with other things
  );


【讨论】:

以上是关于Angular等待所有订阅完成的主要内容,如果未能解决你的问题,请参考以下文章

在 Angular 中拉取和订阅 firebase 数据

Angular Universal Render 等待 Http 结果 Observable 订阅者

Angular 2 - 如何在等待 http 请求完成时阻止 GUI 渲染

Angular 2+等待方法/可观察完成

等待异步函数在 Angular 中完成

Angular forEach 等到所有承诺都完成?