5

我有一个关于使用 Angular 和 RxJs 的 takeUntil 运算符取消订阅的常见模式之一的问题。在这篇文章中,它在第三个位置。例如,我们在组件类中有这样的代码:

  private destroy$: Subject<boolean> = new Subject();

  ngOnInit() {
     this.control.
     .pipe(takeUntil(this.destroy$)
     .subscribe(doSmthngFunc); 
  }

  ngOnDestroy() {
    this.destroy$.next(true);
    // Which next line of code is correct?
    // this.destroy$.complete()     // this one?
    // this.destroy$.unsubscribe()  // or this one?
  }

第一行 this.destroy$.next(true) 是完全清楚的。但第二个不是。如果我们研究这些方法的实现,我们会发现它们的行为有些相似。 完成(): 取消订阅():

据我了解,在语义上 complete() 更可取,因为我们在组件生命周期中第一次和最后一次调用 next(),然后我们完成了这个 Subject,将其视为 Observable 并可以调用 complete()。这些方法属于观察者,取消订阅属于可观察对象,我们没有要取消订阅的订阅。但在底层,这些方法有类似的代码:

    this.isStopped = true; // both

    this.observers.length = 0; // complete
    this.observers = null;     // unsubscribe

    this.closed = true;        // only unsubscribe

理论上,complete() 具有延迟效果,因为它可能会在每个订阅的观察者上调用 complete(),但我们在 destroy$ 上没有观察者。那么问题来了——哪种方式更可取,更不容易出错,为什么?

4

3 回答 3

1

组件的破坏是一个单一的事件。

   this.destroy$.next();
   this.destroy$.complete();

确保主题只发出一次完成

例如;

    const destroy$ = new Subject();

    destroy$.subscribe(v => console.log("destroyed"));

    destroy$.next();
    destroy$.complete();
    destroy$.next();

    // the above prints "destroyed" only once.

完成不是技术要求,但如果您不这样做,则依赖于完成而不是发射的业务逻辑将不会总是有效,并且可能会泄漏内存。

例如,以下将是 RxJs 中的内存泄漏。

   destroyed$.subscribe(() => {
       console.log('This might leak memory');
   });

以上可能会泄漏内存,因为订阅永远不会结束,并且 observable 永远不会完成。您可以通过添加first()操作员或确保主题完成来修复泄漏。RxJS 不知道主题只会发出一个值,所以你必须告诉它。否则订阅者仍然绑定到堆栈帧并且不会被垃圾收集。因此,虽然垃圾收集器可能会在使用组件后收集组件,如果有任何东西引用了订阅者的堆栈帧,那么该订阅将继续存在。

所以在你的销毁对象上调用完成,这样其他人就不会犯错误。

this.destroy$.unsubscribe()

对主题调用取消订阅可能不会对创建内部订阅的下游运算符产生影响。例如,switchMap()创建mergeMap()内部订阅。

因此,您无法有效地管理更高级别的订阅。最好取消订阅调用该subscribe()方法时创建的订阅,因为这是运算符链中的最后一个。

于 2019-07-06T11:14:21.317 回答
0

我不认为你需要做更多的事情destroy$.next();destroy$Subject您在课堂上创建的,它唯一负责的就是中止您的订阅。没有人,但你的班级可以触摸它(因为它是私人的)

根据文章,最好做一个destroy$.complete()以避免内存泄漏。我认为使用unsubscribe()它没有任何意义。如果有人订阅了destroy$,您应该将其存储在订阅中,并在调用者方法中取消订阅ngOnDestroy()。但是,由于您使用takeUntil的是 ,因此无需取消订阅。

于 2019-07-05T21:02:28.600 回答
-1

两种说法都会产生预期的效果。Afaik 没有技术理由不使用unsubscribe. 语义上——正如你所提到的——使用它更有意义complete

Subject 既是可观察的(发送)又是观察者(收听),这种二元性在这里得到体现。next两者complete都属于事物的“发送”方面。complete信号“此流将不会发送任何进一步的值”。unsubscribe是“侦听”界面的一部分,并具有相应不同的含义:“不要通知我任何进一步的排放”。

编辑:在重新阅读时,我发现您已经在问题中包含了这种区别,所以我的回答可能不会为您增加太多:(。我确实认为语义本身就足够重要,可以使用完全取消订阅在这里,并且看不到在这种模式中使用一个而不是另一个的实际风险。希望这仍然有些帮助!

于 2019-07-05T23:15:45.397 回答