我应该在NgOnDestroy生命周期中何时存储Subscription
实例并调用unsubscribe()
,何时可以忽略它们?
保存所有订阅会在组件代码中引入很多混乱。
HTTP Client Guide忽略这样的订阅:
getHeroes() {
this.heroService.getHeroes()
.subscribe(
heroes => this.heroes = heroes,
error => this.errorMessage = <any>error);
}
最终,我们会在其他地方导航。路由器将从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();
}
答案 0 :(得分:863)
最近一集Adventures in Angular Ben Lesh和Ward Bell讨论了如何/何时取消订阅组件的问题。讨论从大约1:05:30开始。
沃德提到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,作为了解何时完成作为组件维护的Observable的方式内部状态。
也就是说,我们现在主要需要解决方案,所以这里有一些其他资源。
来自RxJs核心团队成员Nicholas Jamieson的takeUntil()
模式建议以及帮助实施该模式的tslint规则。 https://blog.angularindepth.com/rxjs-avoiding-takeuntil-leaks-fb5182d047ef
轻量级npm包,它公开一个Observable运算符,该运算符将一个组件实例(this
)作为参数,并在ngOnDestroy
期间自动取消订阅。
https://github.com/NetanelBasal/ngx-take-until-destroy
如果您没有进行AOT构建(但我们现在都应该进行AOT),上面的另一种变体与人体工程学稍微好一些。 https://github.com/smnbbrv/ngx-rx-collector
自定义指令*ngSubscribe
,其工作方式与异步管道类似,但在模板中创建了一个嵌入式视图,因此您可以参考“已解包”&#39;整个模板的价值。
https://netbasal.com/diy-subscription-handling-directive-in-angular-c8f6e762697f
我在评论中提到尼古拉斯&#39;博客过度使用takeUntil()
可能表示您的组件尝试执行过多操作,并且将现有组件分为功能和演示组件被考虑。然后,您可以将功能组件中的Observable | async
转换为演示组件的Input
,这意味着在任何地方都不需要订阅。阅读有关此方法的更多信息here
我在NGConf与Ward Bell谈到了这个问题(我甚至向他展示了这个答案,他说这是正确的)但是他告诉我Angular的文档团队解决了这个未发表的问题(虽然他们正在研究获得批准)。他还告诉我,我可以通过即将发布的官方建议来更新我的答案。
我们今后应该使用的解决方案是向所有在其类代码中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
运算符添加为最后一个运算符非常重要,以防止运算符链中的中间observable泄漏。
来源5
Angular教程,路由章节现在陈述如下:&#34;路由器管理它提供的可观察量并本地化订阅。当组件被销毁时,清理订阅,防止内存泄漏,因此我们不需要取消订阅路径参数Observable。&#34; - Mark Rajcok
这里有关于路由器可观测量的Angular文档的Github问题discussion,其中Ward Bell提到所有这些的澄清正在进行中。
来源4
在此video from NgEurope中,Rob Wormald还表示您无需取消订阅Router Observables。他还在此video from November 2016中提到http
服务和ActivatedRoute.params
。
<强> TLDR:强>
对于这个问题,有(2)种Observables
- 有限值和无限值。
http
Observables
生成有限(1)值,类似DOM event listener
Observables
生成无限值。
如果您手动拨打subscribe
(不使用异步管道),则来自无限 unsubscribe
的{{1}}。
不要担心有限,Observables
会照顾它们。
来源1
我在Angular的Gitter here中找到了Rob Wormald的回答。
他说(我为了清晰而重组,重点是我的)
如果单值序列(如http请求) 手动清理是不必要的(假设您手动在控制器中订阅)
我应该说&#34;如果它的序列完成&#34; (其中单值序列,一个la http,是一个)
如果它是无限序列,你应该取消订阅异步管道为你做的
他还在youtube video中提到了RxJs
{Oberables} they clean up after themselves
......在Observables的上下文中complete
(就像Promises一样,因为它们总是产生1个值,所以总是完成结束 - 我们从不担心取消订阅Promises以确保他们清理xhr
事件监听器,对吧?)。
来源2
同样在Rangle guide to Angular 2中读取
在大多数情况下,我们不需要显式调用unsubscribe方法,除非我们想要提前取消或我们的Observable的寿命比我们的订阅更长。 Observable操作符的默认行为是在发布.complete()或.error()消息后立即处理订阅。请记住,RxJS旨在用于“火灾和忘记”。时尚大部分时间。
短语our Observable has a longer lifespan than our subscription
什么时候适用?
当在Observable
完成之前销毁(或之前很长时间)之前销毁的组件内创建订阅时适用。
如果我们订阅http
请求或发出10个值的observable并且在http
请求返回或发出10个值之前销毁了我们的组件,那么我认为这是有意义的,我们是还好!
当请求返回或最终发出第10个值时,Observable
将完成,所有资源都将被清除。
来源3
如果我们从同一个Rangle指南中查看this example,我们可以看到Subscription
到route.params
确实需要unsubscribe()
,因为我们不知道何时那些params
将停止更改(发出新值)。
组件可以通过导航来销毁,在这种情况下路由参数可能仍会改变(它们可能在技术上改变直到应用程序结束)并且订阅中分配的资源仍然会被分配,因为还没有completion
。
答案 1 :(得分:71)
您不需要手动进行大量订阅和取消订阅。使用RxJS.Subject和takeUntil组合来处理像老板这样的订阅:
import {Subject} from "rxjs/Subject";
@Component(
{
moduleId: __moduleName,
selector: 'my-view',
templateUrl: '../views/view-route.view.html',
}
)
export class ViewRouteComponent implements OnDestroy
{
componentDestroyed$: Subject<boolean> = new Subject();
constructor(protected titleService: TitleService)
{
this.titleService.emitter1$
.takeUntil(this.componentDestroyed$)
.subscribe(
(data: any) =>
{
// ... do something 1
}
);
this.titleService.emitter2$
.takeUntil(this.componentDestroyed$)
.subscribe(
(data: any) =>
{
// ... do something 2
}
);
// ...
this.titleService.emitterN$
.takeUntil(this.componentDestroyed$)
.subscribe(
(data: any) =>
{
// ... do something N
}
);
}
ngOnDestroy()
{
this.componentDestroyed$.next(true);
this.componentDestroyed$.complete();
}
}
建议by @acumartini in comments提出的替代方法使用takeWhile代替takeUntil。您可能更喜欢它,但请注意,这样您的Observable执行将不会在组件的ngDestroy上被取消(例如,当您耗费时间计算或等待来自服务器的数据时)。基于takeUntil的方法没有这个缺点,导致立即取消请求。 Thanks to @AlexChe for detailed explanation in comments
所以这是代码:
@Component(
{
moduleId: __moduleName,
selector: 'my-view',
templateUrl: '../views/view-route.view.html',
}
)
export class ViewRouteComponent implements OnDestroy
{
alive: boolean = true;
constructor(protected titleService: TitleService)
{
this.titleService.emitter1$
.takeWhile(() => this.alive)
.subscribe(
(data: any) =>
{
// ... do something 1
}
);
this.titleService.emitter2$
.takeWhile(() => this.alive)
.subscribe(
(data: any) =>
{
// ... do something 2
}
);
// ...
this.titleService.emitterN$
.takeWhile(() => this.alive)
.subscribe(
(data: any) =>
{
// ... do something N
}
);
}
// Probably, this.alive = false MAY not be required here, because
// if this.alive === undefined, takeWhile will stop. I
// will check it as soon, as I have time.
ngOnDestroy()
{
this.alive = false;
}
}
答案 2 :(得分:56)
Subscription类有一个有趣的功能:
表示可支配资源,例如执行Observable。订阅有一个重要的方法,即取消订阅,不需要参数,只需处理订阅所持有的资源 此外,订阅可以通过add()方法组合在一起,该方法会将子订阅附加到当前订阅。当订阅被取消订阅时,其所有子女(及其孙子女)也将被取消订阅。
您可以创建一个聚合订阅对象,该对象将您的所有订阅分组。
您可以通过创建一个空的Subscription并使用其add()
方法向其添加订阅来完成此操作。当您的组件被销毁时,您只需要取消订阅聚合订阅。
@Component({ ... })
export class SmartComponent implements OnInit, OnDestroy {
private subscriptions = new Subscription();
constructor(private heroService: HeroService) {
}
ngOnInit() {
this.subscriptions.add(this.heroService.getHeroes().subscribe(heroes => this.heroes = heroes));
this.subscriptions.add(/* another subscription */);
this.subscriptions.add(/* and another subscription */);
this.subscriptions.add(/* and so on */);
}
ngOnDestroy() {
this.subscriptions.unsubscribe();
}
}
答案 3 :(得分:23)
一些有关在Angular组件中取消可观察项的最佳实践:
订阅组件中的可观察对象时,几乎总是安排在组件被破坏时退订。
有一些不需要观察的例外观察点。可观察到的ActivatedRoute除外。
ActivatedRoute及其可观察物与路由器本身绝缘。当不再需要路由组件并且注入的ActivatedRoute随之死亡时,Router会销毁它。
随时可以退订。这是无害的,绝不是坏习惯。
并响应以下链接:
http
observable 我收集了一些有关Angular组件中的可观察项取消订阅的最佳实践,以与您分享:
http
可以观察到的取消订阅是有条件的,我们应该考虑在逐个销毁组件之后运行“订阅回调”的影响。我们知道,角度退订并清除http
可观察的(1),(2)本身。尽管从资源的角度来看这是正确的,但这只能说明一半。假设我们正在谈论直接从组件内部调用http
,并且http
响应花费的时间超过了所需时间,因此用户关闭了该组件。即使关闭并销毁了组件,subscribe()
处理程序仍将被调用。这可能会产生有害的副作用,在更糟的情况下,会使应用程序状态中断。如果回调中的代码尝试调用刚刚处理过的内容,也会导致异常。但是,有时偶尔需要它们。例如,假设您正在创建一个电子邮件客户端,并且在电子邮件发送完成后触发了声音-即使组件关闭(8),您仍然希望这种声音发生。AsyncPipe
,因为它会自动取消订阅可观察到的组件破坏。ActivatedRoute
之类的route.params
观察对象,因为只要父//主机组件存在。正如Routing & Navigation
文档中上面引述中所述,在其他情况下,无需取消订阅它们。OnDestroy
生命周期钩子取消订阅该生命周期钩子,根据文档,该钩子将在服务被销毁时调用。takeUntil
(3)管理订阅,也可以使用package中提到的npm
(4) The easiest way to unsubscribe from Observables in Angular。FormGroup
和form.valueChanges
之类的form.statusChanges
Renderer2
之类的renderer2.listen
服务的可观察对象HostListener
之类的事件,因为angular会在需要时非常注意删除事件侦听器,并防止由于事件绑定而导致任何潜在的内存泄漏。 一个不错的最终提示:如果您不确定某个可观察对象是否正在自动取消订阅/完成,请向complete
添加一个subscribe(...)
回调并检查是否当组件被销毁时会被调用。
答案 4 :(得分:14)
这取决于。如果通过调用someObservable.subscribe()
,您开始占用一些必须在组件的生命周期结束时手动释放的资源,那么您应该调用theSubscription.unsubscribe()
以防止内存泄漏。
让我们仔细看看你的例子:
getHero()
会返回http.get()
的结果。如果您查看角度2 source code,http.get()
会创建两个事件侦听器:
_xhr.addEventListener('load', onLoad);
_xhr.addEventListener('error', onError);
并通过调用unsubscribe()
,您可以取消请求以及侦听器:
_xhr.removeEventListener('load', onLoad);
_xhr.removeEventListener('error', onError);
_xhr.abort();
请注意,_xhr
是特定于平台的,但我认为在您的情况下假设它是XMLHttpRequest()
是安全的。
通常情况下,这足以保证手动unsubscribe()
电话。但是根据这个WHATWG spec,XMLHttpRequest()
一旦被“完成”就会被垃圾收集,即使附加了事件监听器也是如此。所以我想这就是为什么angular 2官方指南省略unsubscribe()
并让GC清理听众。
至于你的第二个例子,它取决于params
的实现。截至今天,角度官方指南不再显示取消订阅params
。我再次调查了src,发现params
只是一个BehaviorSubject。由于没有使用事件监听器或定时器,并且没有创建全局变量,因此省略unsubscribe()
应该是安全的。
你的问题的底线是始终调用unsubscribe()
作为内存泄漏防范,除非你确定observable的执行不会创建全局变量,添加事件监听器,设置定时器,或者做任何导致内存泄漏的事情。
如有疑问,请查看该观察结果的实施情况。如果observable在其unsubscribe()
中写了一些清理逻辑,这通常是构造函数返回的函数,那么你有充分的理由认真考虑调用unsubscribe()
。
答案 5 :(得分:6)
Angular 2官方文档提供了何时取消订阅以及何时可以安全忽略的说明。看看这个链接:
https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service
查找标题为父级和子级通过服务进行通信的段落,然后是蓝色框:
请注意,我们捕获订阅并在销毁AstronautComponent时取消订阅。这是一个内存泄漏保护步骤。此应用程序中没有实际风险,因为AstronautComponent的生命周期与应用程序本身的生命周期相同。在更复杂的应用程序中,这并非总是如此。
我们不会将此保护添加到MissionControlComponent,因为作为父级,它控制MissionService的生命周期。
我希望这会对你有所帮助。
答案 6 :(得分:4)
基于:Using Class inheritance to hook to Angular 2 component lifecycle
另一种通用方法:
export abstract class UnsubscribeOnDestroy implements OnDestroy {
protected d$: Subject<any>;
constructor() {
this.d$ = new Subject<void>();
const f = this.ngOnDestroy;
this.ngOnDestroy = () => {
f();
this.d$.next();
this.d$.complete();
};
}
public ngOnDestroy() {
// no-op
}
}
&#13;
并使用:
@Component({
selector: 'my-comp',
template: ``
})
export class RsvpFormSaveComponent extends UnsubscribeOnDestroy implements OnInit {
constructor() {
super();
}
ngOnInit(): void {
Observable.of('bla')
.takeUntil(this.d$)
.subscribe(val => console.log(val));
}
}
&#13;
答案 7 :(得分:3)
由于seangwright的解决方案(编辑3)似乎非常有用,我还发现将此功能打包到基本组件中很麻烦,并且暗示其他项目团队成员记得在ngOnDestroy上调用super()来激活此功能。 / p>
这个答案提供了一种从超级调用中解脱出来的方法,并使“componentDestroyed $”成为基本组件的核心。
class BaseClass {
protected componentDestroyed$: Subject<void> = new Subject<void>();
constructor() {
/// wrap the ngOnDestroy to be an Observable. and set free from calling super() on ngOnDestroy.
let _$ = this.ngOnDestroy;
this.ngOnDestroy = () => {
this.componentDestroyed$.next();
this.componentDestroyed$.complete();
_$();
}
}
/// placeholder of ngOnDestroy. no need to do super() call of extended class.
ngOnDestroy() {}
}
然后您可以自由使用此功能,例如:
@Component({
selector: 'my-thing',
templateUrl: './my-thing.component.html'
})
export class MyThingComponent extends BaseClass implements OnInit, OnDestroy {
constructor(
private myThingService: MyThingService,
) { super(); }
ngOnInit() {
this.myThingService.getThings()
.takeUntil(this.componentDestroyed$)
.subscribe(things => console.log(things));
}
/// optional. not a requirement to implement OnDestroy
ngOnDestroy() {
console.log('everything works as intended with or without super call');
}
}
答案 8 :(得分:3)
正式的编辑#3答案(和变体)运作良好,但让我感觉到的是围绕可观察订阅的业务逻辑的“混乱”。
这是使用包装器的另一种方法。
Warining:实验代码
文件 subscribeAndGuard.ts 用于创建一个新的Observable扩展来包装.subscribe()
并在其中包裹ngOnDestroy()
。
用法与.subscribe()
相同,但引用该组件的其他第一个参数除外。
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
const subscribeAndGuard = function(component, fnData, fnError = null, fnComplete = null) {
// Define the subscription
const sub: Subscription = this.subscribe(fnData, fnError, fnComplete);
// Wrap component's onDestroy
if (!component.ngOnDestroy) {
throw new Error('To use subscribeAndGuard, the component must implement ngOnDestroy');
}
const saved_OnDestroy = component.ngOnDestroy;
component.ngOnDestroy = () => {
console.log('subscribeAndGuard.onDestroy');
sub.unsubscribe();
// Note: need to put original back in place
// otherwise 'this' is undefined in component.ngOnDestroy
component.ngOnDestroy = saved_OnDestroy;
component.ngOnDestroy();
};
return sub;
};
// Create an Observable extension
Observable.prototype.subscribeAndGuard = subscribeAndGuard;
// Ref: https://www.typescriptlang.org/docs/handbook/declaration-merging.html
declare module 'rxjs/Observable' {
interface Observable<T> {
subscribeAndGuard: typeof subscribeAndGuard;
}
}
这是一个包含两个订阅的组件,一个包含包装,另一个包含。唯一需要注意的是必须实现OnDestroy (如果需要,还有空体),否则Angular不知道调用包装版本。
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/Rx';
import './subscribeAndGuard';
@Component({
selector: 'app-subscribing',
template: '<h3>Subscribing component is active</h3>',
})
export class SubscribingComponent implements OnInit, OnDestroy {
ngOnInit() {
// This subscription will be terminated after onDestroy
Observable.interval(1000)
.subscribeAndGuard(this,
(data) => { console.log('Guarded:', data); },
(error) => { },
(/*completed*/) => { }
);
// This subscription will continue after onDestroy
Observable.interval(1000)
.subscribe(
(data) => { console.log('Unguarded:', data); },
(error) => { },
(/*completed*/) => { }
);
}
ngOnDestroy() {
console.log('SubscribingComponent.OnDestroy');
}
}
演示插件是here
附加说明: 重新编辑3 - “官方”解决方案,这可以通过在订阅之前使用takeWhile()而不是takeUntil(),以及在ngOnDestroy中使用简单的布尔而不是另一个Observable来简化。
@Component({...})
export class SubscribingComponent implements OnInit, OnDestroy {
iAmAlive = true;
ngOnInit() {
Observable.interval(1000)
.takeWhile(() => { return this.iAmAlive; })
.subscribe((data) => { console.log(data); });
}
ngOnDestroy() {
this.iAmAlive = false;
}
}
答案 9 :(得分:3)
按照@seangwright的回答,我写了一个抽象类来处理组件中“无限”observables的订阅:
subscribe()
要使用它,只需将其扩展到角度组件中,然后按如下方式调用this.subscribe(someObservable, data => doSomething());
方法:
super.ngOnDestroy()
它也像往常一样接受错误和完整的回调,一个观察者对象,或者根本不接受回调。如果您还在子组件中实现该方法,请记得调用"NaN"
。
在这里找到Ben Lesh的另一个参考:RxJS: Don’t Unsubscribe。
答案 10 :(得分:2)
我喜欢最后两个答案,但如果子类在"this"
中引用ngOnDestroy
,我会遇到问题。
我将其修改为此,看起来它解决了这个问题。
export abstract class BaseComponent implements OnDestroy {
protected componentDestroyed$: Subject<boolean>;
constructor() {
this.componentDestroyed$ = new Subject<boolean>();
let f = this.ngOnDestroy;
this.ngOnDestroy = function() {
// without this I was getting an error if the subclass had
// this.blah() in ngOnDestroy
f.bind(this)();
this.componentDestroyed$.next(true);
this.componentDestroyed$.complete();
};
}
/// placeholder of ngOnDestroy. no need to do super() call of extended class.
ngOnDestroy() {}
}
答案 11 :(得分:2)
我尝试了seangwright的解决方案(编辑3)
对于由计时器或间隔创建的Observable不起作用。
然而,我通过另一种方法使其工作:
import { Component, OnDestroy, OnInit } from '@angular/core';
import 'rxjs/add/operator/takeUntil';
import { Subject } from 'rxjs/Subject';
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/Rx';
import { MyThingService } from '../my-thing.service';
@Component({
selector: 'my-thing',
templateUrl: './my-thing.component.html'
})
export class MyThingComponent implements OnDestroy, OnInit {
private subscriptions: Array<Subscription> = [];
constructor(
private myThingService: MyThingService,
) { }
ngOnInit() {
const newSubs = this.myThingService.getThings()
.subscribe(things => console.log(things));
this.subscriptions.push(newSubs);
}
ngOnDestroy() {
for (const subs of this.subscriptions) {
subs.unsubscribe();
}
}
}
答案 12 :(得分:1)
,对于每个 subscribe ,您需要取消订阅。优势=>防止状态变得过重。
例如: 在component1中:
import {UserService} from './user.service';
private user = {name: 'test', id: 1}
constructor(public userService: UserService) {
this.userService.onUserChange.next(this.user);
}
服务中:
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
public onUserChange: BehaviorSubject<any> = new BehaviorSubject({});
在component2中:
import {Subscription} from 'rxjs/Subscription';
import {UserService} from './user.service';
private onUserChange: Subscription;
constructor(public userService: UserService) {
this.onUserChange = this.userService.onUserChange.subscribe(user => {
console.log(user);
});
}
public ngOnDestroy(): void {
// note: Here you have to be sure to unsubscribe to the subscribe item!
this.onUserChange.unsubscribe();
}
答案 13 :(得分:1)
对于在发出诸如AsyncSubject
之类的结果之后立即完成的可观察对象,例如对于来自HTTP请求的可观察对象,您无需取消订阅。
调用它们unsubscribe()
并没有什么坏处,但是如果可观察值是closed
,则退订方法will simply not do anything:
if (this.closed) {
return;
}
当您的长期观测对象随时间推移会发出多个值(例如BehaviorSubject
或ReplaySubject
)时,您需要取消订阅以防止内存泄漏。
您可以使用管道运算符轻松创建一个可观察到的对象,该对象在发出如此长寿命的可观察对象的结果后立即完成。
在这里的一些答案中,提到了take(1)
管道。但我更喜欢the first()
pipe。与take(1)
的不同之处在于它将:
如果在发送下一个通知之前观察者已完成,则向观察者的错误回调传递
EmptyError
。
第一个管道的另一个优点是,您可以传递一个谓词,该谓词将帮助您返回满足某些条件的第一个值:
const predicate = (result: any) => {
// check value and return true if it is the result that satisfies your needs
return true;
}
observable.pipe(first(predicate)).subscribe(observer);
First会在发出第一个值之后(或在传递函数参数时第一个满足您的谓词的值)直接完成,因此无需取消订阅。
有时候,您不确定自己是否可以长寿。我并不是说这是一个好习惯,但是您可以随时添加first
管道,以确保不需要手动取消订阅。在可观察对象上添加仅发射一个值的附加first
管道并没有什么坏处。
在开发过程中,您可以使用the single
pipe,如果可观察的源发出多个事件,则将失败。这可以帮助您探索可观察的类型,以及是否有必要取消订阅。
observable.pipe(single()).subscribe(observer);
first
和single
看起来非常相似,两个管道都可以采用可选谓词,但是区别很重要,并且在this stackoverflow answer here中进行了很好的总结:
第一
第一项出现时将立即发射。将在此之后完成。
单
如果可观察的源发出多个事件,则将失败。
注意 我在尝试回答官方文档时尽可能做到准确和完整,但是如果缺少重要内容,请发表评论... < / p>
答案 14 :(得分:1)
如果需要取消订阅,则可以使用以下可观察管道方法的运算符
import { Observable, Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { OnDestroy } from '@angular/core';
export const takeUntilDestroyed = (componentInstance: OnDestroy) => <T>(observable: Observable<T>) => {
const subjectPropertyName = '__takeUntilDestroySubject__';
const originalOnDestroy = componentInstance.ngOnDestroy;
const componentSubject = componentInstance[subjectPropertyName] as Subject<any> || new Subject();
componentInstance.ngOnDestroy = (...args) => {
originalOnDestroy.apply(componentInstance, args);
componentSubject.next(true);
componentSubject.complete();
};
return observable.pipe(takeUntil<T>(componentSubject));
};
它可以这样使用:
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
@Component({ template: '<div></div>' })
export class SomeComponent implements OnInit, OnDestroy {
ngOnInit(): void {
const observable = Observable.create(observer => {
observer.next('Hello');
});
observable
.pipe(takeUntilDestroyed(this))
.subscribe(val => console.log(val));
}
ngOnDestroy(): void {
}
}
操作员包装组件的ngOnDestroy方法。
重要提示:操作员应该是可观察管道中的最后一位。
答案 15 :(得分:1)
上述情况的另一个简短补充是:
答案 16 :(得分:1)
当组件被销毁时,您通常需要取消订阅,但Angular将会越来越多地处理它,例如在Angular4的新的次要版本中,他们有此部分用于路由取消订阅:
您需要取消订阅吗?
如下所述 ActivatedRoute:路线信息部分的一站式服务 路由&amp;导航页面,路由器管理它的可观察量 提供和本地化订阅。订阅是 组件被破坏时清理,防止内存 泄漏,因此您无需取消订阅路径paramMap 可观察到的。
下面的例子是一个很好的例子,从Angular创建一个组件并在之后销毁它,看看组件如何实现OnDestroy,如果你需要onInit,你也可以在你的组件中实现它,比如implements OnInit, OnDestroy
import { Component, Input, OnDestroy } from '@angular/core';
import { MissionService } from './mission.service';
import { Subscription } from 'rxjs/Subscription';
@Component({
selector: 'my-astronaut',
template: `
<p>
{{astronaut}}: <strong>{{mission}}</strong>
<button
(click)="confirm()"
[disabled]="!announced || confirmed">
Confirm
</button>
</p>
`
})
export class AstronautComponent implements OnDestroy {
@Input() astronaut: string;
mission = '<no mission announced>';
confirmed = false;
announced = false;
subscription: Subscription;
constructor(private missionService: MissionService) {
this.subscription = missionService.missionAnnounced$.subscribe(
mission => {
this.mission = mission;
this.announced = true;
this.confirmed = false;
});
}
confirm() {
this.confirmed = true;
this.missionService.confirmMission(this.astronaut);
}
ngOnDestroy() {
// prevent memory leak when component destroyed
this.subscription.unsubscribe();
}
}
答案 17 :(得分:0)
对于处理订阅,我使用“取消订阅者”类。
这是退订类。
export class Unsubscriber implements OnDestroy {
private subscriptions: Subscription[] = [];
addSubscription(subscription: Subscription | Subscription[]) {
if (Array.isArray(subscription)) {
this.subscriptions.push(...subscription);
} else {
this.subscriptions.push(subscription);
}
}
unsubscribe() {
this.subscriptions
.filter(subscription => subscription)
.forEach(subscription => {
subscription.unsubscribe();
});
}
ngOnDestroy() {
this.unsubscribe();
}
}
并且您可以在任何组件/服务/效果等中使用此类。
示例:
class SampleComponent extends Unsubscriber {
constructor () {
super();
}
this.addSubscription(subscription);
}
答案 18 :(得分:0)
您可以使用最新的 Subscription
类以不太混乱的代码退订Observable。
我们可以使用normal variable
来执行此操作,但是在每个新的订阅中它将使用override the last subscription
,因此请避免这种情况,当您处理更多数量的Obseravable和更多类型的Obseravable时,此方法非常有用 BehavoiurSubject
和 Subject
订阅
表示可抛弃的资源,例如Observable的执行。订阅具有一种重要的方法,即取消订阅,该方法不带任何参数,而是仅处理订阅所拥有的资源。
您可以通过两种方式使用它,
您可以直接将订阅推送到Subscription Array
subscriptions:Subscription[] = [];
ngOnInit(): void {
this.subscription.push(this.dataService.getMessageTracker().subscribe((param: any) => {
//...
}));
this.subscription.push(this.dataService.getFileTracker().subscribe((param: any) => {
//...
}));
}
ngOnDestroy(){
// prevent memory leak when component destroyed
this.subscriptions.forEach(s => s.unsubscribe());
}
使用add()
中的 Subscription
subscriptions = new Subscription();
this.subscriptions.add(subscribeOne);
this.subscriptions.add(subscribeTwo);
ngOnDestroy() {
this.subscriptions.unsubscribe();
}
Subscription
可以保留子订阅,并可以安全地取消订阅所有子订阅。此方法处理可能的错误(例如,如果任何子订阅为空)。
希望这会有所帮助..:)
答案 19 :(得分:0)
SubSink软件包,一种简单,一致的退订解决方案
正如没有人提到的那样,我想推荐Ward Bell创建的Subsink软件包:https://github.com/wardbell/subsink#readme。
我一直在一个项目上使用它,因为我们有几个开发人员都在使用它。在每种情况下都有一种一致的工作方式很有帮助。
答案 20 :(得分:0)
订阅实际上仅具有unsubscribe()函数以释放资源或取消可观察的执行。 在Angular中,销毁组件时必须取消订阅Observable。幸运的是,Angular有一个ngOnDestroy挂钩,该挂钩在组件被销毁之前会被调用,这使开发人员可以在此处提供清理人员,以避免挂起订阅,打开门户以及将来可能发生的不利情况,从而将我们咬在后面。>
@Component({...})
export class AppComponent implements OnInit, OnDestroy {
subscription: Subscription
ngOnInit () {
var observable = Rx.Observable.interval(1000);
this.subscription = observable.subscribe(x => console.log(x));
}
ngOnDestroy() {
this.subscription.unsubscribe()
}
}
我们在我们的AppCompoennt中添加了ngOnDestroy,并在this.subscription Observable上调用了unsubscribe方法
如果有多个订阅:
@Component({...})
export class AppComponent implements OnInit, OnDestroy {
subscription1$: Subscription
subscription2$: Subscription
ngOnInit () {
var observable1$ = Rx.Observable.interval(1000);
var observable2$ = Rx.Observable.interval(400);
this.subscription1$ = observable.subscribe(x => console.log("From interval 1000" x));
this.subscription2$ = observable.subscribe(x => console.log("From interval 400" x));
}
ngOnDestroy() {
this.subscription1$.unsubscribe()
this.subscription2$.unsubscribe()
}
}
答案 21 :(得分:0)
就我而言,我使用的是@seanwright 提出的解决方案的变体:
https://github.com/NetanelBasal/ngx-take-until-destroy
这是在 ngx-rocket / starter-kit 项目中使用的文件。您可以在此处访问它until-destroyed.ts
组件看起来像这样
/**
* RxJS operator that unsubscribe from observables on destory.
* Code forked from https://github.com/NetanelBasal/ngx-take-until-destroy
*
* IMPORTANT: Add the `untilDestroyed` operator as the last one to
* prevent leaks with intermediate observables in the
* operator chain.
*
* @param instance The parent Angular component or object instance.
* @param destroyMethodName The method to hook on (default: 'ngOnDestroy').
*/
import { untilDestroyed } from '../../core/until-destroyed';
@Component({
selector: 'app-example',
templateUrl: './example.component.html'
})
export class ExampleComponent implements OnInit, OnDestroy {
ngOnInit() {
interval(1000)
.pipe(untilDestroyed(this))
.subscribe(val => console.log(val));
// ...
}
// This method must be present, even if empty.
ngOnDestroy() {
// To protect you, an error will be thrown if it doesn't exist.
}
}
答案 22 :(得分:0)
这里有很多很棒的答案...
让我添加另一种选择:
import { interval } from "rxjs";
import { takeUntil } from "rxjs/operators";
import { Component } from "@angular/core";
import { Destroyable } from "@bespunky/angular-zen/core";
@Component({
selector: 'app-no-leak-demo',
template: '? Destroyable component rendered. Unload me and watch me cleanup...'
})
export class NoLeakComponent extends Destroyable
{
constructor()
{
super();
this.subscribeToInterval();
}
private subscribeToInterval(): void
{
const value = interval(1000);
const observer = {
next : value => console.log(`? Destroyable: ${value}`),
complete: () => console.log('? Observable completed.')
};
// ==== Comment one and uncomment the other to see the difference ====
// Subscribe using the inherited subscribe method
this.subscribe(value, observer);
// ... or pipe-in the inherited destroyed subject
//value.pipe(takeUntil(this.destroyed)).subscribe(observer);
}
}
组件/服务扩展 Destroyable
(来自名为 @bespunky/angular-zen
的库)。
该类现在可以简单地使用 this.subscribe()
或 takeUntil(this.destroyed)
,无需任何额外的样板代码。
安装库使用:
> npm install @bespunky/angular-zen
答案 23 :(得分:0)
这是我对这个问题的看法,为了让我的生活保持简单,我选择了在组件被销毁时取消订阅的手动方式。
为此,我创建了一个名为 Subscriptor 的类,它主要包含静态成员,即:
subscriptor.ts
import { Subscription } from "rxjs";
export class Subscriptor {
private static subscriptions: Subscription[] = [];
static set subscription(subscription: Subscription) {
Subscriptor.subscriptions.push(subscription);
}
static unsubscribe() {
Subscriptor.subscriptions.forEach(subscription => subscription ? subscription.unsubscribe() : 0);
Subscriptor.subscriptions = [];
}
}
组件内部的用法如下:
当你想订阅任何服务时,只需将订阅放到订阅者的 setter 中即可。
ngOnInit(): void {
Subscriptor.subscription = this.userService.getAll().subscribe(users => this.users = users);
Subscriptor.subscription = this.categoryService.getAll().subscribe(categories => this.categories = categories);
Subscriptor.subscription = this.postService.getAll().subscribe(posts => this.posts = posts);
}
当您想取消订阅任何服务时,只需调用 Subscriptor 的取消订阅方法即可。
ngOnDestroy(): void {
Subscriptor.unsubscribe();
}
答案 24 :(得分:0)
出于性能原因,始终建议从可观察订阅中取消订阅以避免内存泄漏,并且有不同的方法可以做到这一点,
顺便说一下,我阅读了大部分答案,但我没有找到谈论 async
管道的人,推荐 Rxjs
模式与 Angular 应用程序因为它提供了自动订阅和离开时会被销毁的组件的订阅:
请找出如何实现的示例
app.component.ts:
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { BookService } from './book.service';
import { Book } from './book';
@Component({
selector: 'app-observable',
templateUrl: './observable.component.html'
})
export class AppComponent implements OnInit {
books$: Observable<Book[]>
constructor(private bookService: BookService) { }
ngOnInit(): void {
this.books$ = this.bookService.getBooksWithObservable();
}
}
app.component.html:
<h3>AsyncPipe with Promise using NgFor</h3>
<ul>
<li *ngFor="let book of books$ | async" >
Id: {{book?.id}}, Name: {{book?.name}}
</li>
</ul>
答案 25 :(得分:-1)
-更新Angular 9和Rxjs 6解决方案
unsubscribe
角组件生命周期中使用ngDestroy
class SampleComponent implements OnInit, OnDestroy {
private subscriptions: Subscription;
private sampleObservable$: Observable<any>;
constructor () {}
ngOnInit(){
this.subscriptions = this.sampleObservable$.subscribe( ... );
}
ngOnDestroy() {
this.subscriptions.unsubscribe();
}
}
takeUntil
class SampleComponent implements OnInit, OnDestroy {
private unsubscribe$: new Subject<void>;
private sampleObservable$: Observable<any>;
constructor () {}
ngOnInit(){
this.subscriptions = this.sampleObservable$
.pipe(takeUntil(this.unsubscribe$))
.subscribe( ... );
}
ngOnDestroy() {
this.unsubscribe$.next();
this.unsubscribe$.complete();
}
}
ngOnInit
处调用的某些操作,该操作仅在组件初始化时发生一次。class SampleComponent implements OnInit {
private sampleObservable$: Observable<any>;
constructor () {}
ngOnInit(){
this.subscriptions = this.sampleObservable$
.pipe(take(1))
.subscribe( ... );
}
}
我们还有async
管道。但是,这是在模板上使用的(不是在Angular组件中使用)。