【问题标题】:Unsubscribe in subscribe callback function?在订阅回调函数中取消订阅?
【发布时间】:2019-04-12 11:29:23
【问题描述】:

我一直在寻找一种简单的方法来避免我所读到的由于取消订阅失败而导致的内存泄漏。大多数时候,我只想要后端的 ONE 响应。然后我想退订。那么为什么不在回调中调用呢?

  onSubmit(){
    var subscription = this.puzzleService.login(this.nameoremail, this.password).subscribe( success =>{
      if(success){
        this.router.navigate(['/puzzles']);
      }
      else{
        this.message="Login failed. Please try again.";
      }
      this.loading=false;
      subscription.unsubscribe();
    });
    this.loading=true;
  }

注意将订阅分配给局部变量。然后这个局部变量被锁定在一个闭包中,并在其工作完成时被告知取消订阅。没有类变量,没有 takeUntil,没有别的。

它编译并运行没有错误。我对调试器不够熟悉,无法确定 observable 对象是否实际被销毁并随后被垃圾收集。

我有什么遗漏吗?更熟悉调试器的人可以纠正我吗?因为如果这行得通,我将在任何地方都这样做。除了在我的 pollWords() 函数中...

这似乎比我见过的其他解决方案要简单得多。我想我什至不需要闭包,因为当我在调试器中查看它时,我看到“_this”是“this”的闭包,而“this”实际上是可观察的。因此,如果有某种方法可以防止“this”发生的修改,那么我可以调用“this.unsubscribe()”并完成。并不是说对象引用闭包是一件可怕的事情......

参考资料:

Angular/RxJs When should I unsubscribe from `Subscription`

Rxjs Observable Lifecycle

【问题讨论】:

标签: angular rxjs


【解决方案1】:

一种更符合 RxJS 风格的方式是使用 take 运算符,如下所示:

this.puzzleService.login(this.nameoremail, this.password).pipe(
  take(1)
).subscribe(success => {
  // ...
});

这将使 observable 只发出一个值,然后完成它。

但是,@Picci 是完全正确的,如果您的 login 方法返回来自 HttpClient 请求的结果,您无需手动取消订阅(就此而言,也无需使用 take)。

【讨论】:

  • 供将来参考:A Rejected Edit 指出“文章建议永远不必取消订阅从 HttpClient 返回的 Observables。这在某些情况下是短视的建议"; 此编辑旨在解决帖子的作者,作为编辑毫无意义。它应该写成评论或答案。
【解决方案2】:

取消订阅对于避免内存泄漏很重要,但如果您“只想要后端的 ONE 响应”,它可能就不那么重要了。

我的意思是,如果您在 this.puzzleService.login 后面使用 Angular http 服务,那么您不必担心取消订阅,因为服务本身会在收到后端响应后立即取消订阅,或发生错误。

当您面对 Observable 流时,取消订阅很重要,因为 Observable 流本质上会发出多个值,这不是 http 调用的情况,但可能是 web-sockets 流或其他类型的流的情况。

article from Ben Lesh,即使不是 RxJS 的领导者,也是最大的贡献者,对这个话题有所启发。

【讨论】:

    【解决方案3】:

    我认为应该可行且“合法”的结构是这样的:

    onSubmit() {
        const subscription = this.puzzleService.login(this.nameoremail, this.password)
            .subscribe( success => {
                // your code
                // more of your code
                this.loading = false;
                setTimeout(() => subscription.unsubscribe(), 0);
            });
        this.loading = true;
    }
    

    现在.unsubscribe().subscribe(...)中的代码之后执行,表示订阅存在,可以退订。

    当 Observable 在同一个组件中创建时,.take(1) 将起作用,我认为如果 Observable 不是你的,你就不能结束它,例如当在另一个组件或服务中创建/管理时,因为可能还有其他订阅者。那么你真的只想结束你自己的订阅,而不是完成 Observable 本身。

    【讨论】:

      【解决方案4】:

      这是一个非常棘手的问题 - 我有时也会取消订阅局部变量,但我认为这种方式并不像看起来那么合适。 您可以在此处阅读有关响应式 Fox 的文章中的所有极端情况(保存对此的引用、组件变量等):https://medium.com/angular-in-depth/why-you-have-to-unsubscribe-from-observable-92502d5639d0

      【讨论】:

      • 请不要进行外部链接,除非它直接支持您已在答案中添加的内容。除了您的意见之外,您没有在答案中添加任何内容。
      猜你喜欢
      • 2016-02-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-19
      • 2017-12-18
      • 2012-03-14
      • 2016-07-27
      • 1970-01-01
      相关资源
      最近更新 更多