RXJS - Angular - 取消订阅 Subjects

17

此线程所述,取消订阅Angular 5+中的Observables的“官方”解决方案通常是使用takeUntil。目前为止,一切正常。我的问题是,如果我订阅的Observable实际上是Subject,这个解决方案是否也适用?


2
没关系,因为Subject扩展了Observable类。这同样适用于EventEmitter,因为它扩展了Subject类。 - martin
1
正如马丁所说,是的。基本上,无论您订阅什么,都应该在某个时间点销毁该订阅(取消订阅),以避免内存泄漏。 - David Anthony Acosta
此外,OnDestroy是一个很好的实现方式,用于销毁组件中的订阅。http://brianflove.com/2016/12/11/anguar-2-unsubscribe-observables/ - ShellNinja
1
此外,将takeUntil放置在最后一个操作符,以避免取消订阅的混乱:https://blog.angularindepth.com/rxjs-avoiding-takeuntil-leaks-fb5182d047ef - Davy
2个回答

39

当你在任何东西上调用.subscribe()(包括主题),需要确保取消订阅。

处理有限可观察对象: 如果您订阅了一个有限的可观察对象(即具有有限/有限序列的可观察对象),则最后一条消息将发送结束信号,并且订阅将自动取消。 以下是此类的示例:

Observable.of(100)
Observable.from([1,2,3,4])

无限可观测量的例子包括:

Observable.fromEvent(document, 'click')
Observable.timer(1000)

调用/管道.first().take(number).takeWhile(某个条件在某一点上将评估为false)takeUntil(发出值的可观察对象)会将原本无限的可观察对象转换为有限的。

不要再使用.subscribe()方法: 另一种避免取消订阅的流行方法是首先不进行订阅。这听起来可能很愚蠢,因为什么时候您需要一个没有进行订阅的可观察对象呢?如果您只需要将一些数据传递给视图/HTML模板,则将该可观察对象管道化到异步管道中,将取消订阅问题传递给异步管道本身。

在HTML模板中的典型示例:

<h1>Editing {{ infiniteObservable$ | async }}<h1>
<li *ngFor="let user of userObservable$ | async as users; index as i; first as isFirst">
   {{i}}/{{users.length}}. {{user}} <span *ngIf="isFirst">default</span>
</li>

手动取消订阅: 最后,您可以选择保留所有订阅的引用。您不需要保留指向每个订阅的变量,只需使用单个Subscription对象来跟踪所有订阅,然后一次性取消订阅所有订阅即可。

以下是示例:
const subscriptions = new Subscription();
subscriptions.add(observable1$.subscribe());
subscriptions.add(observable2$.subscribe());
subscriptions.unsubscribe();

快速总结,如何处理取消订阅,以下任何一种方法:
1.将无限可观察对象转换为有限的可观察对象,从而消除取消订阅的需要(在 ngOnDestroy() 中使用.takeUntil(this.destroyed$)并执行this.destroyed$.next())。
2.避免订阅,并通过async管道传递可观察对象。
3.保留任何订阅的引用,并在ngOnDestroy()方法中调用.unsubscribe()
个人倾向于仅使用前两种方法之一。

1
谢谢提供信息。我在我指向的线程中读到,为什么你会使用takeWhile()而不是takeUntil()呢?xxx - didgewind
1
哦,这应该没关系,我猜takeUntil会更标准,因为布尔变量开始时为false(默认值),然后翻转为true,而不是相反的方式。 - Per Hornshøj-Schierbeck
@didgewind 两个操作符都将无限可观测对象转换为有限的,这意味着您不需要取消订阅。选择您认为更易于阅读的操作符(当您或其他人需要维护您的代码时)。 - Per Hornshøj-Schierbeck
@PerHornshøj-Schierbeck 如果您想在一个订阅后立即从app.component.ts中的activatedRoute queryParams中“取消订阅”,该怎么办? - candidJ
所以,HTTP调用是一个有限的可观察对象吗? - kuldeep
@kuldeep 是的,我认为所有的调用都是(至少是常见的,比如get/post),因为它只触发一次,然后Angular处理取消订阅。 - Per Hornshøj-Schierbeck

2

我有一些要补充的内容。 Subject 内部存储订阅者(Observable 也是如此)。如果 Subject 是你的组件的一部分(在组件内创建、存储为属性或闭包中)则该主题及其订阅将随着组件本身一起被垃圾回收。

但这是一个特例,需要非常小心:一切必须包含在组件中。

例如,如果 FormControl.valueChanges 只用于组件中,则可以放心地从中取消订阅。

但为了保险起见,如果不想考虑这个问题,只需使用 takeUntil


1
请问您能否指向一些文档,展示了Angular组件的清理,包括取消订阅Subjects的方法? - Stevy
在组件的onDestroy中真的不需要显式调用mySubject.unsubscribe吗? - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接