-编辑4-其他资源(2018/09/01)
在最近的《 Angular Adventures》一集中,Ben Lesh和Ward Bell讨论了如何/何时取消订阅组件中的问题。讨论从大约1:05:30开始。
沃德(Ward)right now there's an awful takeUntil dance that takes a lot of machinery
和斋(Shai Reznik)提到Angular handles some of the subscriptions like http and routing
。
作为回应,Ben提到,现在正在进行讨论,以允许Observables参与Angular组件的生命周期事件,Ward建议组件可以订阅的Observable生命周期事件,以了解何时完成以组件内部状态维护的Observables。
就是说,我们现在最需要解决方案,因此这里有一些其他资源。
takeUntil()
RxJ的核心团队成员Nicholas Jamieson 对这种模式的建议以及一条有助于实施的tslint规则。https://ncjamieson.com/avoiding-takeuntil-leaks/
轻量级npm软件包,它公开一个Observable运算符,该运算符将组件实例(this
)作为参数,并在期间自动退订ngOnDestroy
。
https://github.com/NetanelBasal/ngx-take-until-destroy
如果您不进行AOT构建(但我们现在都应该进行AOT),则上述方法的另一个变化是人体工程学要好一些。
https://github.com/smnbbrv/ngx-rx-collector
自定义指令的*ngSubscribe
工作方式类似于异步管道,但在模板中创建了嵌入式视图,因此您可以在整个模板中引用“未包装”值。
https://netbasal.com/diy-subscription-handling-directive-in-angular-c8f6e762697f
我在Nicholas博客的评论中提到,过度使用takeUntil()
可能表明您的组件正在尝试做太多事情,应该考虑将现有组件分为功能和演示组件。然后| async
,您可以将“可观察对象”从“功能”组件转换Input
为“演示”组件的,这意味着在任何地方都不需要订阅。在此处阅读有关此方法的更多信息
-编辑3-``官方''解决方案(2017/04/09)
我在NGConf上与Ward Bell谈到了这个问题(我什至向他展示了这个答案,他说的是正确的),但他告诉我Angular的文档小组对这个问题尚未解决(尽管他们正在努力使它得到批准) )。他还告诉我,我可以通过即将提出的官方建议来更新我的SO答案。
今后我们应该使用的解决方案是在其类代码内向调用的private ngUnsubscribe = new Subject();
所有组件中添加一个字段。.subscribe()
Observable
然后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();
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
运算符添加为最后一个,以防止运算符链中的中间可观察对象泄漏。
-编辑2(2016/12/28)
来源5
Angular教程的“路由”一章现在指出以下内容:“路由器管理它提供的可观察对象并本地化订阅。在销毁组件时清理订阅,以防止内存泄漏,因此我们无需取消订阅路线参数可观察到。” - 马克Rajcok
这是有关Router Observables的Angular文档的Github问题的讨论,Ward Bell提到正在为所有这些澄清。
-编辑1
来源4
在NgEurope的这段视频中, Rob Wormald还说您不需要取消订阅Router Observables。他还在2016年11月的这段视频中提到了该http
服务。ActivatedRoute.params
---原始答案
TLDR:
对于此问题,有(2)种Observables
- 有限值和无限值。
http
Observables
产生有限(1)值,类似DOM的东西event listener
Observables
产生无限值。
如果您手动调用subscribe
(不使用异步管道),则unsubscribe
来自infinite Observables
。
不用担心有限的,RxJs
会照顾他们的。
来源1
我在这里从Angular的Gitter中找到了 Rob Wormald的答案。
他指出(为清晰起见,我进行了重组,重点是我的)
如果它是单值序列(例如http请求),则不需要手动清理(假设您手动订阅了控制器)
我应该说“如果它是一个完成的序列 ”(其中一个单值序列,例如la http,是一个)
如果它是无限序列,则应退订异步管道为您执行的操作
他还在这个关于Observables的youtube视频中提到they clean up after themselves
...在Observables 的上下文中complete
(例如Promises,由于它们始终产生1值并结束,所以总是完成-我们从不担心取消Promises的订阅以确保其清理xhr
事件)听众,对吗?)。
来源2
同样在Angular 2的Rangle指南中
在大多数情况下,除非我们想提早取消或Observable的寿命比订阅的寿命长,否则我们无需显式调用unsubscribe方法。Observable运算符的默认行为是在发布.complete()或.error()消息后立即处理订阅。请记住,RxJS被设计为大多数时候以“即弃即用”的方式使用。
该短语何时our Observable has a longer lifespan than our subscription
适用?
它适用于在组件内部创建订阅的情况,该订阅在Observable
完成之前被销毁(或在此之前不是“长”)。
我的意思是,如果我们订阅一个http
发出10个值的请求或一个可观察对象,并且在该http
请求返回或发出10个值之前销毁了我们的组件,那么我们还是可以的!
当请求确实返回或最终发出第十个值时,Observable
将完成并清除所有资源。
来源3
如果我们从相同的Rangle指南中查看此示例,则可以看到Subscription
to route.params
确实需要一个,unsubscribe()
因为我们不知道它们何时params
会停止更改(发出新值)。
可以通过导航销毁该组件,在这种情况下,路由参数可能仍会更改(从技术上讲,它们可能会更改,直到应用终止),并且由于没有,分配的资源仍将分配completion
。
Subscription
shttp-requests
可以忽略,因为它们只调用onNext
一次,然后调用onComplete
。将Router
改为调用onNext
多次,并且可能永远不会调用onComplete
(不知道这一点......)。Observable
s与Event
s 相同。所以我想那些应该是unsubscribed
。