使用Subject时如何避免内存泄漏?RxJS订阅清理问题解决
问题场景
我创建了一个Observable,在点击事件中执行取消订阅操作。直接使用Observable时,能成功取消订阅并清除定时器;但通过Subject中转Observable后,Observable创建时的清理匿名函数没执行,导致内存泄漏,该怎么处理?
核心代码
let newObs = new Observable<number>(observer=>{ let i=0; let interval = setInterval((f:any)=>{ i++; console.log(i+"in observer"); observer.next(i) },1000) return ()=>{ console.log("will be executed when unsubscribed or on error"); clearInterval(interval); } }) let sub = new Subject<number>(); newObs.subscribe(sub); this.subscription2 = sub.subscribe({ next: (data: number) => { console.log(data+" subscription2") }, error: (error: string) => { console.log(error) }, complete: ()=>{console.log('complete')} }) this.subscription = sub.subscribe(f=>{ console.log(f+"in subscription"); },error=>{ console.log(error) },()=>{ console.log("complete") })
点击取消订阅代码
ngAfterViewInit(){ fromEvent(this.mapm.nativeElement,'click').subscribe(res=>{ console.log(res) this.subscription.unsubscribe(); this.subscription2.unsubscribe(); },(error:any)=>{ console.log(error) },()=>{ console.log("completed!") }) }
解决方法
问题根源是:你只取消了Subject下游的两个订阅,却没取消newObs.subscribe(sub)这一步的上游订阅——Observable的清理函数只有当它自己的订阅被取消时才会执行,所以定时器一直运行导致内存泄漏。
方式一:手动管理上游订阅
把newObs到Subject的订阅对象保存起来,点击取消时一起销毁:
// 新增变量保存上游订阅 this.sourceSub = newObs.subscribe(sub); // 点击取消时补充取消上游订阅 ngAfterViewInit(){ fromEvent(this.mapm.nativeElement,'click').subscribe(res=>{ console.log(res) this.subscription.unsubscribe(); this.subscription2.unsubscribe(); this.sourceSub.unsubscribe(); // 关键:取消上游订阅触发清理函数 },(error:any)=>{ console.log(error) },()=>{ console.log("completed!") }) }
方式二:用takeUntil统一管理所有订阅(推荐)
创建一个销毁信号Subject,所有关联的订阅都通过takeUntil绑定,点击时发送销毁信号,一次性终止所有订阅,包括上游的Observable订阅:
// 组件内声明销毁信号 private destroy$ = new Subject<void>(); // 初始化订阅时绑定销毁信号 ngOnInit() { let newObs = new Observable<number>(observer=>{ // 原Observable逻辑不变 }) let sub = new Subject<number>(); // 上游订阅绑定销毁信号 newObs.pipe(takeUntil(this.destroy$)).subscribe(sub); // 下游两个订阅也绑定销毁信号 this.subscription2 = sub.pipe(takeUntil(this.destroy$)).subscribe({ next: (data: number) => { console.log(data+" subscription2") }, error: (error: string) => { console.log(error) }, complete: ()=>{console.log('complete')} }) this.subscription = sub.pipe(takeUntil(this.destroy$)).subscribe(f=>{ console.log(f+"in subscription"); },error=>{ console.log(error) },()=>{ console.log("complete") }) } // 点击时触发销毁信号 ngAfterViewInit(){ fromEvent(this.mapm.nativeElement,'click').subscribe(res=>{ console.log(res) this.destroy$.next(); // 发送销毁信号终止所有绑定的订阅 this.destroy$.complete(); // 完成Subject释放资源 },(error:any)=>{ console.log(error) },()=>{ console.log("completed!") }) } // 组件销毁时也触发销毁,双重保障避免内存泄漏 ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); }
这种方式不用逐个管理订阅对象,代码更简洁,也能避免遗漏导致的内存泄漏,是Angular中管理订阅的常用最佳实践。
内容的提问来源于stack exchange,提问作者SameerShaik
相关产品推荐
相关产品推荐

