【问题标题】:Angular When to unsubscribe()?Angular何时取消订阅()?
【发布时间】:2019-06-12 23:39:03
【问题描述】:

所以,我对 unsubscribe() 方法有点不清楚。

在 ngOnDestroy() 生命周期方法中似乎是合适的地方?所以我在下面的代码中做到了。但是,deleteFile() 方法现在不会执行了吗?

我在另一个帖子中阅读了以下内容:(这是正确的吗?)

“你不应该手动取消订阅,90% 的时间。在这个特定的 情况下,您正在使用服务来获取数据。如果此服务使用 http 调用,因此 HttpClient,observable 已经关闭 一旦调用完成并且关闭的 observables 已经存在 已取消订阅。”

那么我是否需要在这里取消订阅?谢谢

...
export class UploadComponent implements OnInit, OnDestroy {
 private subscriptions: Subscription;
...

ngOnDestroy() {
 this.subscriptions.unsubscribe();
}

deleteFile(id: any) {
 if (window.confirm("Are you sure you want to delete this file?")) {

  const sub = this.fileCabinetService.deleteFile(id)
    .subscribe(
      res => {

        ...
      },
      error => {

        ...
      }
    );

  this.subscriptions.add(sub);
 }
}

【问题讨论】:

    标签: angular


    【解决方案1】:

    您误解了Subscriptionadd 的工作原理。

    当你subscribe 到某个东西时,你会得到一个Subscription 对象,你可以调用unsubscribe()。这可以。但是,您没有正确分配值。您将Subscription 分配给const sub,然后您将其作为completion 块传递给add

    Subscription.add 视为try/catchfinally 块。不管Subscription 的结果是什么,当它是complete 时,传递给add 的块都会执行。将此用于任何清理任务。

    subscriptions: Subscriptions[] = [];
    
    ngOnDestroy() {
       subscriptions.forEach((sub) => sub.unsubscribe());
    }
    
    deleteFile(id: any) {
       const sub = this.fileCabinetService.deleteFile(id).subscribe(...);
       this.subscriptions.push(sub);
       sub.add(() => this.subscriptions.remove(sub));
    }
    

    要回答您关于何时使用unsubscribe 的问题,这实际上取决于deleteFile 的作用。如果deleteFile 方法在内部用一个值(或一组值)发出信号然后完成,则订阅将自动终止。如果它没有完成并悬空,那么您的订阅将继续存在。

    考虑以下场景:

    WallClock.subscribe((time) => console.log(time));
    

    这个Subscription 永远不会被终止,因为时间(大概)会无限期地继续下去。相反,您需要手动控制何时终止。您可以通过以下几种方式做到这一点:

    /* Get the current time, but don't bother listening to updates. */
    WallClock.pipe(take(1)).subscribe((time) => console.log(time));
    
    /* Continually update with the current time as long as the component is on screen
       — Requires setting `this.staySubscribed = false` when you're leaving */
    WallClock.pipe(takeWhile(() => this.staySubscribed)).subscribe((time) => console.log(time));
    
    /* Continually update until you remember to unsubscribe
       — Requires remembering to unsubscribe and can get verbose with multiple subscriptions
       - Call `this.subscription.unsubscribe()` later */
    this.subscription = WallClock.subscribe((time) => console.log(time));
    

    如果您的deleteFile 像这样操作并不断报告值而没有明确的完成条件,您应该确保以某种方式终止订阅。很可能(基于函数的名称)这是一个自动终止的Subscription,不需要您做任何事情。如果您想真正安全,pipe(take(1)) 将为您提供保障。

    【讨论】:

      【解决方案2】:

      关于取消订阅,您应该查看this answer 中概述的takeUntil 模式。这是取消订阅和避免内存泄漏的首选方式。您可以将代码重构为如下所示:

      ...
      export class UploadComponent implements OnInit, OnDestroy {
         private ngUnsubscribe = new Subject();
      ...
      
      ngOnDestroy() {
         this.ngUnsubscribe.next();
         this.ngUnsubscribe.complete();
      }
      
      deleteFile(id: any) {
         if (window.confirm("Are you sure you want to delete this file?")) {
      
         this.fileCabinetService.deleteFile(id)
           .pipe(takeUntil(this.ngUnsubscribe))
           .subscribe(
             res => {
              ...
            },
            error => {
              ...
            }
          );
       }
      }
      
      

      话虽如此,如果您订阅了@angular/common/http observable,则无需取消订阅,因为http 服务在返回数据时会调用complete()。详情请看this implementation in the xhr back-end

      if (response.ok) {
        responseObserver.next(response);
        // TODO(gdi2290): defer complete if array buffer until done
        responseObserver.complete();
        return;
      }
      

      所以,对于@angular/common/http 请求,不要担心。对于大多数其他人,请遵循takeUntil 模式。

      【讨论】:

      • 假设FileCabinetServicedeleteFile(id)方法中直接从HTTP服务返回Observable,那么observable object已经完成并且不需要unsubscribe调用?
      • @Itanex 正确。该框架将为您complete您的可观察对象,因此您不必取消订阅。也就是说,如果用户导航到新页面,我通常想取消我的 HTTP 调用。因此,出于这个原因,我通常使用takeUntil 模式,但这取决于被调用的页面/路由。
      • 我对 RXJS 的使用并不像大多数人那样贪婪,我最近才将它融入我的开发技能中。我见过takeUntil 并了解它的功能。虽然直到最近,当我需要订阅取消订阅时,我通常会执行以下操作 const sub = myFunction().subscribe(() => sub.unsubscribe())。这显然是一个亮点,而不是整个代码。在执行诸如将带外的删除/更新发送到角度生命周期挂钩之类的操作时,我会使用此模式。或 ASYNC 管道。事实证明,这种情况很少而且相差甚远
      猜你喜欢
      • 2023-01-04
      • 2018-04-19
      • 2021-11-18
      • 1970-01-01
      • 2020-04-07
      • 1970-01-01
      • 2016-06-15
      • 2021-02-25
      • 2019-02-11
      相关资源
      最近更新 更多