Angular/RxJs我应该何时取消订阅`
在Ngondestory生命周期中,我应该何时存储Angular/RxJs我应该何时取消订阅`,angular,rxjs,observable,subscription,angular-component-life-cycle,Angular,Rxjs,Observable,Subscription,Angular Component Life Cycle,在Ngondestory生命周期中,我应该何时存储订阅实例并调用取消订阅(),何时可以忽略它们 保存所有订阅会给组件代码带来很多混乱 忽略以下订阅: import { Component, OnDestroy, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; @Component({ template: '<div></div>' }) export class SomeComponen
订阅
实例并调用取消订阅()
,何时可以忽略它们
保存所有订阅会给组件代码带来很多混乱
忽略以下订阅:
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 {
}
}
getHeroes(){
this.heroService.getheros()
.订阅(
英雄=>这个。英雄=英雄,
error=>this.errorMessage=表示:
最终,我们将导航到其他地方。路由器将从DOM中删除此组件并销毁它。我们需要在这之前进行清理。具体来说,我们必须在Angular销毁该组件之前取消订阅。否则可能会造成内存泄漏
我们在Ngondestory
方法中取消订阅我们的Observable
private sub:any;
恩戈尼尼特(){
this.sub=this.route.params.subscribe(params=>{
让id=+params['id'];//(+)将字符串'id'转换为数字
this.service.getHero(id).then(hero=>this.hero=hero);
});
}
恩贡德斯特罗(){
此.sub.取消订阅();
}
Angular 2官方文档解释了何时取消订阅以及何时可以安全地忽略。请查看此链接:
查找标题为“家长与子女通过服务进行沟通”的段落,然后查找蓝色框:
请注意,我们捕获订阅并在组件销毁时取消订阅。这是一个内存泄漏保护步骤。此应用程序中没有实际风险,因为组件的生存期与应用程序本身的生存期相同。在更复杂的应用程序中,情况并非总是如此
我们不将此防护添加到MissionControlComponent,因为作为父级,它控制MissionService的生存期
我希望这对您有所帮助。这取决于情况。如果通过调用someObservable.subscribe()
,您开始占用一些资源,这些资源必须在组件的生命周期结束时手动释放,那么您应该调用subscription.unsubscribe()
,以防止内存泄漏
让我们仔细看看您的示例:
getHero()
返回http.get()
的结果。如果查看angular 2,http.get()
将创建两个事件侦听器:
_xhr.addEventListener('load', onLoad);
_xhr.addEventListener('error', onError);
_xhr.removeEventListener('load', onLoad);
_xhr.removeEventListener('error', onError);
_xhr.abort();
通过调用unsubscribe()
,您可以取消请求以及侦听器:
_xhr.addEventListener('load', onLoad);
_xhr.addEventListener('error', onError);
_xhr.removeEventListener('load', onLoad);
_xhr.removeEventListener('error', onError);
_xhr.abort();
请注意,\uxhr
是特定于平台的,但我认为可以安全地假设它是一个XMLHttpRequest()
通常情况下,这足以证明手动调用unsubscribe()
是正确的。但根据这一点,一旦XMLHttpRequest()
完成,即使有事件侦听器附加到它,它也会被垃圾收集。所以我想这就是为什么angular 2官方指南省略了unsubscribe()
并让GC清理侦听器
至于你的第二个例子,它取决于params
的实现。从今天起,angular官方指南不再显示从params
取消订阅。我再次查看,发现params
只是一个a。因为没有使用事件侦听器或计时器,也没有创建全局变量,它应该是be省略取消订阅()
是安全的
您的问题的底线是,始终调用unsubscribe()
,以防止内存泄漏,除非您确定observable的执行不会创建全局变量、添加事件侦听器、设置计时器或执行任何其他导致内存泄漏的操作
当有疑问时,查看可观察的实现。如果观察者已经将一些清理逻辑写入其<代码> un订阅()>代码>,这通常是构造函数返回的函数,那么您有充分的理由认真考虑调用<代码> un订阅()< <代码> > < /p> ---编辑4个附加资源。(2018/09/01)
在Ben Lesh和Ward Bell最近的一集中,他们讨论了如何/何时取消订阅组件的问题。讨论大约在1:05:30开始
Ward提到现在有一个可怕的takeUntil舞蹈,需要很多机器,Shai Reznik提到Angular处理一些订阅,比如http和路由
作为回应,Ben提到目前正在进行讨论,以允许可观察到的组件生命周期事件与角度组件生命周期事件挂钩,Ward建议组件可以订阅生命周期事件的可观察事件,作为了解何时完成作为组件内部状态维护的可观察事件的一种方式
也就是说,我们现在主要需要解决方案,所以这里有一些其他资源
RxJs核心团队成员Nicholas Jamieson对takeUntil()
模式的建议以及帮助实施该模式的tslint规则
轻量级npm包,公开一个可观察的操作符,该操作符将组件实例(this
)作为参数,并在ngondestory
期间自动取消订阅。
如果您不进行AOT构建(但我们现在都应该进行AOT),则上述方法的另一个变体具有稍微更好的人体工程学。
自定义指令*ngSubscribe
,其工作原理类似于异步管道,但会在模板中创建嵌入式视图,以便您可以在整个模板中引用“unwrapped”值。
我在Nicholas博客的评论中提到,过度使用了takeUntil()
可能表明您的组件试图做得太多,应考虑将现有组件分为功能组件和表示组件。然后,您可以|异步
从功能组件中观察到的内容
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() {}
}
@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;
}
}
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();
}
}
import { OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs/Subscription';
import { Subject } from 'rxjs/Subject';
import { Observable } from 'rxjs/Observable';
import { PartialObserver } from 'rxjs/Observer';
export abstract class InfiniteSubscriberComponent implements OnDestroy {
private onDestroySource: Subject<any> = new Subject();
constructor() {}
subscribe(observable: Observable<any>): Subscription;
subscribe(
observable: Observable<any>,
observer: PartialObserver<any>
): Subscription;
subscribe(
observable: Observable<any>,
next?: (value: any) => void,
error?: (error: any) => void,
complete?: () => void
): Subscription;
subscribe(observable: Observable<any>, ...subscribeArgs): Subscription {
return observable
.takeUntil(this.onDestroySource)
.subscribe(...subscribeArgs);
}
ngOnDestroy() {
this.onDestroySource.next();
this.onDestroySource.complete();
}
}
this.subscribe(someObservable, data => doSomething());
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 {
}
}
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({});
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();
}
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);
}
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());
}
subscriptions = new Subscription();
this.subscriptions.add(subscribeOne);
this.subscriptions.add(subscribeTwo);
ngOnDestroy() {
this.subscriptions.unsubscribe();
}
if (this.closed) {
return;
}
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);
observable.pipe(single()).subscribe(observer);
@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()
}
}
@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()
}
}
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 = [];
}
}
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);
}
ngOnDestroy(): void {
Subscriptor.unsubscribe();
}