【问题标题】:Angular: How to unsubscribe when requesting periodicallyAngular:定期请求时如何取消订阅
【发布时间】:2020-06-16 16:35:53
【问题描述】:

我有一个角度组件,我定期从服务中请求值:

setInterval(() => {
      this.updateValues();
    }, 1000);


updateValues(): void {
  if (this.isValuesUpdateRunning === false) {
    this.isValuesUpdateRunning = true;
    this.apiService.getCurrentValues().subscribe((data: any) => {
      if (data != null) {
        this.currentValues = data;
        this.isValuesUpdateRunning = false;
      }
    });
  }
}

我很确定我应该在某个地方取消订阅该服务,因为我确实通过这样做打开了很多订阅。我正在考虑使用异步管道,但我需要设置“isValueUpdateRunning”变量,如果我只是在模板中使用异步,这是不可能的。

此外,在 Angular 提供的“onDestroy”方法中取消订阅也无济于事,因为据我所知,只有在我离开页面时才会调用此方法。但我需要更频繁地关闭我的订阅,以防止保持数以千计的订阅处于打开状态。

我还尝试在设置我的值后立即关闭订阅,但是我没有在我的视图中获得任何值:

updateValues(): void {
  if (this.isValuesUpdateRunning === false) {
    this.isValuesUpdateRunning = true;
    const sub = this.apiService.getCurrentValues().subscribe((data: any) => {
      if (data != null) {
        this.currentValues = data;
        this.isValuesUpdateRunning = false;
      }
    });
    // closing the subscription right here:
    sub.unsubscribe();
  }
}

你能帮忙吗?我的想法不多了。

【问题讨论】:

  • 如果 this.apiService.getCurrentValues() 正在使用 Angular HttpClient 发出 http 请求,则无需取消订阅。
  • 你没有得到价值,因为你在得到答案之前就退缩了。我同意上述关于 http 请求的评论。
  • @KurtHamilton 我正在使用 HttpClient,是的。这只是一个简单的获取请求。所以你认为我根本不需要退订?我认为除非你使用异步管道,否则它总是需要的。
  • @Manuela 取消订阅并没有什么坏处,事实上,如果您不希望 subscribe 中的逻辑运行,那么如果 api 调用挂起并且您已移动到应用程序的不同部分而不是这是必须的。

标签: angular rxjs


【解决方案1】:

您可以在收到如下所示的响应后关闭订阅。另请注意,如果您从 getCurrentValues() 调用 AJAX API,则 HTTPObservable 是可自动关闭的,您无需关闭订阅,因为一旦您获得了由 Angular HTTP 客户端完成的 http 响应订阅。

 updateValues(): void {
      if (this.isValuesUpdateRunning === false) {
        this.isValuesUpdateRunning = true;
        const sub = this.apiService.getCurrentValues().subscribe((data: any) => {
          if (data != null) {
            this.currentValues = data;
            this.isValuesUpdateRunning = false;
          }
         // closing the subscription right here:
         sub.unsubscribe();
        }, error => { sub.unsubscribe});

      }
    }

【讨论】:

    【解决方案2】:

    假设您在模板中呈现currentValues,我建议使用异步管道。

    isValuesUpdateRunning逻辑而言,您可以使用exhaustMap并将setTimeout替换为RxJS区间

    我在下面放了一个演示 - 我强烈建议学习模拟 api 服务并亲自尝试这些东西。

    const currentValues$ = interval(1000)
      .pipe(
        tap(i => console.log(`timer::${i}`)),
        exhaustMap(i => apiService.getCurrentValues(i)),  // ignores new values until api returns
        tap(apiData => console.log(`api::${apiData}`)),
        filter(data => !!data)                            // filter for truthy values
        takeUntil(stopClick$),                            // or this.destroyed$ component logic
      )
    

    (假设模板中使用了异步管道,例如{{ currentValues$ | async | json }}

    更新 - 最好把 takeUntil 最后!

    Stackblitz

    参考文献

    exhaustmap - 在当前 observable 完成之前忽略新发出的值(角度 http 请求自动完成)

    【讨论】:

      【解决方案3】:

      如果使用 Angular http 客户端,您无需像@Kurt Hamilton 提到的那样取消订阅该服务。您需要停止创建订阅的设置间隔。

      使用 rxjs 运算符的示例。

      interval(1000)
         .pipe(
           takeWhile(() => this.isValuesUpdateRunning), // call subscription until the condition is false
           switchMap(() => this.apiService.getCurrentValues()),
           filter(Boolean), // check if response is not null
           tap(resp => { this.data = resp; this.isValuesUpdateRunning = false; }),
           // handle errors 
      

      【讨论】:

      • 更新了,看来问题是我今天早上没喝咖啡
      • 哈。很抱歉成为那个人,但这将立即取消订阅。 takeUntil 将立即从BehaviorSubject 接收一个值。 condition$ 可以是普通的Subject
      • 更新示例。不,这很好,我有错误的假设,你是完全正确的。检查了文档。也许将 takeWhile 与使用变量的谓词一起使用是正确的方法
      • 我的想法有点不同,我想使用该值不触发订阅。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-06-12
      • 2018-02-14
      • 2020-05-02
      • 1970-01-01
      • 1970-01-01
      • 2019-08-21
      • 2017-04-20
      相关资源
      最近更新 更多