8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

Angular/RxJS 何时应取消订阅“订阅”

Matthias Auswöger 2月前

182 0

我应该何时存储 Subscription 实例并在 ngOnDestroy 生命周期中调用 unsubscribe(),何时可以简单地忽略它们?保存所有订阅会给

我应该在什么时候存储 Subscription unsubscribe() 调用 ngOnDestroy ,什么时候可以简单地忽略它们?

保存所有订阅会给组件代码带来很多混乱。

HTTP 客户端指南 忽略如下订阅:

getHeroes() {
  this.heroService.getHeroes()
                  .subscribe(
                     heroes => this.heroes = heroes,
                     error =>  this.errorMessage = <any>error);
}

同时, Route & Navigation Guide 指出:

最终,我们将导航到其他地方。路由器将从 DOM 中删除此组件并将其销毁。我们需要在发生这种情况之前进行清理。具体来说,我们必须在 Angular 销毁组件之前取消订阅。不这样做可能会造成内存泄漏。

我们 Observable 在方法中 ngOnDestroy

private sub: any;

ngOnInit() {
  this.sub = this.route.params.subscribe(params => {
     let id = +params['id']; // (+) converts string 'id' to a number
     this.service.getHero(id).then(hero => this.hero = hero);
   });
}

ngOnDestroy() {
  this.sub.unsubscribe();
}
帖子版权声明 1、本帖标题:Angular/RxJS 何时应取消订阅“订阅”
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由Matthias Auswöger在本站《angular》版块原创发布, 转载请注明出处!
最新回复 (0)
  • a = ['123', '2', 4]b = a[4] 或 'sss'print b当列表索引超出范围时,我想要获取一个默认值(此处:'sss')。我该怎么做?

    a = ['123', '2', 4]
    b = a[4] or 'sss'
    print b
    

    当列表索引超出范围时,我想获取一个默认值(此处 'sss' :)。

    我怎样才能做到这一点?

  • @gt6707a 流完成(或未完成)与对该完成的任何观察无关。提供给订阅函数的回调(观察者)不确定是否分配了资源。它是对

  • SSB 2月前 0 只看Ta
    引用 4

    本着 Python 的“请求原谅,而不是请求许可”的精神,这里有一种方法:

    try:
        b = a[4]
    except IndexError:
        b = 'sss'
    
  • 长话短说

    对于这个问题,有两种可观察量—— 有限 值和 无限 值。

    http 可观察对象产生 有限 (1)的值,而类似 DOM 事件监听器的可观察对象则产生 无限的 值。

    如果您手动调用 subscribe (不使用异步管道),那么 unsubscribe 将从 无限的 Observables 中调用。

    不要担心 有限的 ,RxJs 会处理它们。


    资料来源:

    1. p6

      p7

      p8

      p9

      p10

      p11

    2. p12

      p13

      p14

      p15

      p16

      p17

    3. 第18页

      第19页

    4. p20

    5. p21

      p22

      p23


    我在 NGConf 上与 Ward Bell 讨论了这个问题(我甚至向他展示了这个答案,他说这是正确的),但他告诉我 Angular 的文档团队对这个问题有一个尚未发布的解决方案(尽管他们正在努力获得批准)。他还告诉我,我可以用即将发布的官方建议更新我的 SO 答案。

    我们今后都应该使用的解决方案是向 private ngUnsubscribe = new Subject<void>(); 在其类代码中调用 Observables .subscribe() 的组件添加一个字段

    然后我们调用 this.ngUnsubscribe.next(); this.ngUnsubscribe.complete(); 我们的 ngOnDestroy() 方法。

    秘诀(正如 @metamaker 在每次调用之前 takeUntil(this.ngUnsubscribe) 调用 .subscribe() ,这将保证在组件被销毁时所有订阅都将被清理。

    例子:

    import { Component, OnDestroy, OnInit } from '@angular/core';
    // RxJs 6.x+ import paths
    import { filter, startWith, takeUntil } from 'rxjs/operators';
    import { Subject } from 'rxjs';
    import { BookService } from '../books.service';
    
    @Component({
        selector: 'app-books',
        templateUrl: './books.component.html'
    })
    export class BooksComponent implements OnDestroy, OnInit {
        private ngUnsubscribe = new Subject<void>();
    
        constructor(private booksService: BookService) { }
    
        ngOnInit() {
            this.booksService.getBooks()
                .pipe(
                   startWith([]),
                   filter(books => books.length > 0),
                   takeUntil(this.ngUnsubscribe)
                )
                .subscribe(books => console.log(books));
    
            this.booksService.getArchivedBooks()
                .pipe(takeUntil(this.ngUnsubscribe))
                .subscribe(archivedBooks => console.log(archivedBooks));
        }
    
        ngOnDestroy() {
            this.ngUnsubscribe.next();
            this.ngUnsubscribe.complete();
        }
    }
    

    注意: 将操作符添加为最后一个非常重要, takeUntil 以防止操作符链中的中间 Observable 发生泄漏。


    最近,在 《Adventures in Angular》 ,Ben Lesh 和 Ward Bell 讨论了如何/何时在组件中取消订阅的问题。讨论开始于 1:05:30 左右。

    Ward 提到 “现在有一个糟糕的 takeUntil 舞蹈,需要很多机器”, 而 Shai Reznik 提到 “Angular 处理一些订阅,如 http 和路由” .

    作为回应,Ben 提到现在正在讨论允许 Observables 挂接到 Angular 组件生命周期事件中,而 Ward 建议使用一个生命周期事件的 Observable,组件可以订阅该 Observable 以此来了解何时完成作为组件内部状态维护的 Observables。

    话虽如此,我们现在最需要的是解决方案,所以这里还有一些其他资源。

    1. p34

    2. p35

    3. p36

    4. p37

    我在 Nicholas 博客的评论中提到,过度使用 takeUntil() 可能表明您的组件试图做太多事情, 应该考虑将 功能演示 | async 功能组件中的 Observable 放入 Input 演示组件中,这意味着任何地方都不需要订阅。 在此处 .

  • 单独调用 complete() 似乎无法清除订阅。但是调用 next() 然后调用 complete() 可以清除订阅,我相信 takeUntil() 只会在生成值时停止,而不会在序列结束时停止。

  • @KennyTM:这是三个不同的选项。我想,这样最好的答案就可以被投票到顶部,而那些糟糕的答案就会被遗忘。这并不是为了引诱他人,而是一种公认​​的做法:meta.stackexchange.com/questions/21761/…

  • @seangwright 使用组件内类型为 Subject 的成员进行快速测试,并使用 ngIf 切换以触发 ngOnInit 和 ngOnDestroy,结果显示,主题及其订阅永远不会完成或被处置(将 finally 操作符连接到订阅)。我必须在 ngOnDestroy 中调用 Subject.complete(),这样订阅才能自行清理。

  • 我希望在列表中也看到字典的 .get 方法,当您使用 get 并且它不存在时,它默认返回 None。

  • 您的 --- 编辑 3 非常有见地,谢谢!我只有一个后续问题:如果使用 takeUnitl 方法,我们就永远不必手动取消订阅任何可观察对象?是这样吗?此外,为什么我们需要在 ngOnDestroy 中调用 next(),为什么不直接调用 complete()?

  • Kay 2月前 0 只看Ta
    引用 11

    本着非 Python 的“请求许可,而不是请求原谅”的精神,这里是另一种方法:

    b = a[4] if len(a) > 4 else 'sss'
    
  • 在 medium.com/@benlesh/rxjs-dont-unsubscribe-6753ed4fda87 的事件背景下讨论的编辑 3

  • 在 Python 中,优化以防止引发异常的唯一技术用例是当 happypath 导致引发异常时。(例如,如果 len(a) 很少 > 4,那么使用 if/then 更便宜。)。如果 happypath 是 len(a) > 4,那么使用上述方法的唯一真正原因是更短的代码(可读性更差的风险),但 maep 使用切片的答案甚至更短......

  • 您无需手动订阅和取消订阅。使用 Subject takeUntil 组合来像老板一样处理订阅:

    import { Subject } from "rxjs"
    import { takeUntil } from "rxjs/operators"
    
    @Component({
      moduleId: __moduleName,
      selector: "my-view",
      templateUrl: "../views/view-route.view.html"
    })
    export class ViewRouteComponent implements OnInit, OnDestroy {
      componentDestroyed$: Subject<boolean> = new Subject()
    
      constructor(private titleService: TitleService) {}
    
      ngOnInit() {
        this.titleService.emitter1$
          .pipe(takeUntil(this.componentDestroyed$))
          .subscribe((data: any) => { /* ... do something 1 */ })
    
        this.titleService.emitter2$
          .pipe(takeUntil(this.componentDestroyed$))
          .subscribe((data: any) => { /* ... do something 2 */ })
    
        //...
    
        this.titleService.emitterN$
          .pipe(takeUntil(this.componentDestroyed$))
          .subscribe((data: any) => { /* ... do something N */ })
      }
    
      ngOnDestroy() {
        this.componentDestroyed$.next(true)
        this.componentDestroyed$.complete()
      }
    }
    

    @acumartini 在评论中 提出了 另一种方法 ,使用 takeWhile 而不是 takeUntil 的 。您可能更喜欢它,但请注意,这样您的 Observable 执行将不会在组件的 ngDestroy 上被取消(例如,当您进行耗时的计算或等待服务器的数据时)。基于 takeUntil 没有这个缺点,并导致请求立即取消。 感谢 @AlexChe 在评论中提供的详细解释 .

    代码如下:

    @Component({
      moduleId: __moduleName,
      selector: "my-view",
      templateUrl: "../views/view-route.view.html"
    })
    export class ViewRouteComponent implements OnInit, OnDestroy {
      alive: boolean = true
    
      constructor(private titleService: TitleService) {}
    
      ngOnInit() {
        this.titleService.emitter1$
          .pipe(takeWhile(() => this.alive))
          .subscribe((data: any) => { /* ... do something 1 */ })
    
        this.titleService.emitter2$
          .pipe(takeWhile(() => this.alive))
          .subscribe((data: any) => { /* ... do something 2 */ })
    
        // ...
    
        this.titleService.emitterN$
          .pipe(takeWhile(() => this.alive))
          .subscribe((data: any) => { /* ... do something N */ })
      }
    
      ngOnDestroy() {
        this.alive = false
      }
    }
    
  • 我记不清来源了,@ThiagoMacedo,但我相信 Python 鼓励 try/except 实现正常流程。这在其他语言中确实看起来不寻常,但没有理由将异常视为某种严重错误,特别是如果您预料到了。您可能有一行代码可能遭受 KeyError 和 IndexError 等错误,并且您可以使用一些异常处理程序清楚快速地处理所有这些异常。这有时可以避免嵌套的 if/then/else 语句反复测试 DivideByZero 和其他此类边缘情况。

  • 我认为使用 takeUntil 和 takeWhile 之间存在显著差异。前者在触发时立即取消订阅源可观察对象,而后者仅在源可观察对象产生下一个值时才取消订阅。如果源可观察对象产生值是一项耗费资源的操作,那么在两者之间进行选择可能超出了风格偏好。请参阅 plunk

  • 我避免使用 try/except 的原因是为了保持代码简洁。在我看来,尝试次数过多会使整个算法变得冗长。现在我同意标准建议是一个不错的选择,但仍然使用许多像这样的单行语句。

  • @AlexChe 感谢您提供有趣的观点!这对于 takeUntil 与 takeWhile 的一般用法非常有效,但对于我们的具体情况则不然。当我们需要在组件销毁时取消订阅监听器时,我们只需检查 takeWhile 中的布尔值,例如 () => alive,因此不会使用任何耗时/耗内存的操作,差异主要在于样式(当然,对于这种特定情况而言)。

  • 本着 Python 的“美丽胜过丑陋”精神

    代码高尔夫方法,使用切片和解包(不确定这在 4 年前是否有效,但它在 python 2.7 + 3.3 中)

    b, = a[4:5] or ['sss']
    

    我认为它比包装函数或 try-catch 要好,但对于初学者来说有点吓人。

    使用切片而不拆包:

    b = a[4] if a[4:] else 'sss'
    

    或者,如果你经常需要这样做,并且不介意编一本字典

    d = dict(enumerate(a))
    b = d.get(4, 'sss')
    
  • A-E 2月前 0 只看Ta
    引用 20

    @metamaker 假设,在我们的组件中,我们订阅了一个 Observable,它在内部挖掘一些加密货币,并为每个挖掘的硬币触发下一个事件,挖掘一个这样的硬币需要一天时间。使用 takeUntil,一旦在组件销毁期间调用 ngOnDestroy,我们将立即取消订阅源挖掘 Observable。因此,挖掘 Observable 函数能够在此过程中立即取消其操作。

返回
作者最近主题: