为了避免组件内部的可观察内存泄漏,我使用 takeUntil()
订阅 Observable 之前的运算符。
我在我的组件中写了这样的东西:
private unsubscribe$ = new Subject();
ngOnInit(): void {
this.http
.get('test')
.pipe(takeUntil(this.unsubscribe$))
.subscribe((x) => console.log(x));
}
ngOnDestroy(): void {
this.unsubscribe$.next();
this.unsubscribe$.complete(); // <--- ??
}
最后我的问题如下:
我需要写
this.unsubscribe$.complete();
或 next()
足够? 是
unsubscribe$
没有完成就会被垃圾收集器抓取? 请解释是否有区别或无关紧要。我不希望我的组件中出现内存泄漏。
最佳答案
简短的回答,不,这不是必需的,但也没有伤害。
长答案:
仅当它阻止垃圾收集时才需要取消订阅/完成 Angular,因为订阅涉及一些主题,这些主题将超过要收集的组件。这就是内存泄漏的产生方式。
如果我有服务:
export class MyService {
mySubject = new Subject();
}
它在 root 中提供并且仅在 root 中提供(这意味着它是一个单例,并且在实例化后永远不会被销毁)和一个注入(inject)此服务并订阅它的主题的组件
export class MyLeakyComponent {
constructor(private myService: MyService) {
this.myService.mySubject.subscribe(v => console.log(v));
}
}
这是造成内存泄漏。为什么?因为MyLeakyComponent中的订阅是被MyService中的subject引用的,所以MyLeakyComponent只要MyService存在并且持有对它的引用就不能被垃圾回收,并且MyService会在应用的生命周期中一直存在。每次实例化 MyLeakyComponent 时都会复合。要解决此问题,您必须取消订阅或在组件中添加终止运算符。
但是这个组件:
export class MySafeComponent {
private mySubect = new Subject();
constructor() {
this.mySubject.subscribe(v => console.log(v));
}
}
是完全安全的,并且会毫无问题地被垃圾收集。没有外部持久实体持有对它的引用。这也是安全的:
@Component({
providers: [MyService]
})
export class MyNotLeakyComponent {
constructor(private myService: MyService) {
this.myService.mySubject.subscribe(v => console.log(v));
}
}
现在注入(inject)的服务由组件提供,因此服务和组件将一起销毁,并且可以安全地进行垃圾回收,因为外部引用也将被销毁。
这也是安全的:
export class MyHttpService { // root provided
constructor(private http: HttpClient) {}
makeHttpCall() {
return this.http.get('google.com');
}
}
export class MyHttpComponent {
constructor(private myhttpService: MyHttpService) {
this.myhttpService.makeHttpCall().subscribe(v => console.log(v));
}
}
因为 http 调用是一类可自终止的 observable,所以它们会在调用完成后自然终止,因此无需手动完成或取消订阅,因为外部引用一旦自然完成就消失了。
至于你的例子:
unsubscribe$
subject 是组件本地的,因此它不可能导致内存泄漏。任何本地主题都是如此。关于最佳实践的说明:
Observables 是复杂的。一个看起来完全安全的,可能以一种微妙的方式涉及一个外部主题。为了完全安全/如果您对 observables 不太满意,通常建议您取消订阅所有非终止 observables。除了您自己花时间做这件事之外,没有其他缺点。我个人觉得 unsubscribe$ 信号方法很老套,并认为它会污染/混淆你的流。对我来说最简单的是这样的:
export class MyCleanedComponent implements OnDestroy {
private subs: Subscription[] = [];
constructor(private myService: MyService) {
this.subs.push(
this.myService.mySubject.subscribe(v => console.log(v)),
this.myService.mySubject1.subscribe(v => console.log(v)),
this.myService.mySubject2.subscribe(v => console.log(v))
);
}
ngOnDestroy() {
this.subs.forEach(s => s.unsubscribe());
}
}
但是,防止泄漏的唯一最佳方法是尽可能使用 angular 提供的异步管道。它为您处理所有订阅管理。
关于angular - 我需要 `complete()` takeUntil ngOnDestroy 内的主题吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57007118/