Angular 12 rxjs嵌套订阅unsubscribe实现方案正确性咨询
问题1解答
你这种把两个订阅赋值给同一个subscription变量的处理方式完全错误。
原因很简单:变量会被后赋值的内容覆盖。你先把initLists()的订阅实例赋值给了this.subscription,之后在initModule()里又把auth.login$的订阅实例赋值给了同一个变量,此时第一个订阅的引用就丢失了。你在ngOnDestroy里只能取消最后赋值的login$订阅,第一个initLists()的订阅永远得不到释放,会造成内存泄漏。
如果要统一管理多个订阅,正确的做法是使用Subscription实例的add()方法,把所有订阅都添加到同一个容器里:
// 初始化的时候直接实例化Subscription private subscriptions = new Subscription(); // 添加第一个订阅 this.subscriptions.add( this.initLists().subscribe(/* 回调逻辑 */) ); // 添加第二个订阅 this.subscriptions.add( this.auth.login$.subscribe(/* 回调逻辑 */) ); // 销毁时统一取消 ngOnDestroy() { this.subscriptions.unsubscribe(); }
问题2解答
两个订阅都需要做取消订阅处理,原因分别如下:
- 第一个
initLists()的订阅:虽然它是由forkJoin包装的HTTP请求,正常请求完成后会自动触发complete结束订阅,但如果用户在请求还未返回时就销毁了Foo组件,此时订阅回调还会执行,会给已经销毁的组件属性赋值,轻则触发不必要的逻辑,重则控制台报错。 - 第二个
auth.login$的订阅:它是基于BehaviorSubject封装的热 observable,本身不会自动触发complete,如果不主动取消订阅,就算Foo组件销毁了,这个订阅的引用还是会被AuthService持有,造成严重的内存泄漏。
额外优化建议
你现有代码里嵌套的httpHandler.getSomeData()订阅也没有做取消处理,同样存在内存泄漏风险,建议尽量避免嵌套订阅,改用switchMap/mergeMap等操作符做流转换,更推荐的Angular订阅管理方式是使用takeUntil操作符配合销毁触发器:
export class Foo implements OnInit, OnDestroy { listA?: KeyValuePair[]; listB?: KeyValuePair[]; private destroy$ = new Subject<void>(); constructor(private httpHandlerCached: HttpHandlerCachedService, private httpHandler: HttpHandlerService, private auth: AuthService) { } ngOnInit(): void { this.initLists() .pipe(takeUntil(this.destroy$)) .subscribe(([listA, listB]) => { this.listA = listA; this.listB = listB; this.auth.login$ .pipe( filter(login => !!login), switchMap(login => this.httpHandler.getSomeData(login.id)), takeUntil(this.destroy$) ) .subscribe(someData => doSomeStuff(someData, listA, listB)); }); } initLists(): Observable<[KeyValuePair[], KeyValuePair[]]> { return forkJoin([ this.httpHandlerCached.getAllListA(), this.httpHandlerCached.getAllListB() ]); } ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); } }
内容的提问来源于stack exchange,提问作者user16831793
相关产品推荐
相关产品推荐

