Angular中嵌套Subscribe导致订阅回调重复触发的原因?
问题:RxJS Subject订阅回调重复触发的原因与解决办法
问题现象
第一次触发onModalClosed时,回调正常执行1次;第二次触发时执行2次;第三次触发时执行3次,次数随触发次数递增。
伪代码展示
主组件核心逻辑
FunctionOne(){ const dialogRef = this.dialog.open(ShowSomethinComponent) const subForDialog = dialogRef.componentInstance.someAction$ .subscribe((response) => { this.openReportIssueModal(response, dialogRef) }); } openReportIssueModal(dialogRef: MatDialogRef<ShowSomethinComponent, any>){ this.reportIssueModalService.onModalClosed.subscribe((data) => { dialogRef.componentInstance.DoSomething(); }) // 服务说明:onModalClosed是全局Subject onModalClosed from service is: onModalClosed = new Subject<boolean>();
触发弹窗的逻辑
private reportMeasurementSubject = new Subject<InspectionMeasurement>(); reportMeasurementAction$ = this.reportMeasurementSubject.asObservable(); openReportIssueModal(meassurement: InspectionItem) { this.reportMeasurementSubject.next(meassurement) }
弹窗服务内部代码
this.reportIssueDialogRef.afterClosed().subscribe((issueSavedSuccessfully: boolean) => { this.onModalClosed.next(issueSavedSuccessfully); })
原因分析
核心问题是订阅未及时取消,导致每次调用openReportIssueModal都会新增一个订阅:
onModalClosed是属于服务的全局Subject,服务实例的生命周期和应用一致,不会随组件/弹窗销毁而消失;- 每次调用
openReportIssueModal,都会给onModalClosed绑定一个新的订阅回调,这些旧订阅从未被销毁; - 后续每次
onModalClosed.next()都会触发所有已存在的订阅回调,次数自然随调用次数递增。
另外,FunctionOne里的subForDialog订阅也没有在弹窗关闭后取消,同样会造成内存泄漏和潜在的重复触发问题。
解决办法
1. 单次触发后自动取消订阅
如果回调只需要执行一次,用take(1)操作符自动完成订阅,避免手动取消:
openReportIssueModal(dialogRef: MatDialogRef<ShowSomethinComponent, any>){ this.reportIssueModalService.onModalClosed.pipe(take(1)) .subscribe((data) => { dialogRef.componentInstance.DoSomething(); }); }
2. 绑定弹窗关闭事件取消订阅
把订阅对象保存下来,在弹窗关闭时手动取消:
openReportIssueModal(dialogRef: MatDialogRef<ShowSomethinComponent, any>){ const sub = this.reportIssueModalService.onModalClosed.subscribe((data) => { dialogRef.componentInstance.DoSomething(); }); // 弹窗关闭时取消当前订阅 dialogRef.afterClosed().subscribe(() => sub.unsubscribe()); }
同时别忘了处理FunctionOne里的订阅:
FunctionOne(){ const dialogRef = this.dialog.open(ShowSomethinComponent) const subForDialog = dialogRef.componentInstance.someAction$ .subscribe((response) => { this.openReportIssueModal(response, dialogRef) }); // 弹窗关闭时取消订阅 dialogRef.afterClosed().subscribe(() => subForDialog.unsubscribe()); }
3. 组件销毁时统一清理订阅
如果组件有生命周期钩子(比如Angular的ngOnDestroy),可以把所有订阅收集到一个Subscription集合里,销毁时统一取消:
// 组件内声明一个订阅集合 private subscriptions = new Subscription(); openReportIssueModal(dialogRef: MatDialogRef<ShowSomethinComponent, any>){ const sub = this.reportIssueModalService.onModalClosed.subscribe((data) => { dialogRef.componentInstance.DoSomething(); }); // 添加到集合中 this.subscriptions.add(sub); } // 组件销毁时统一取消所有订阅 ngOnDestroy() { this.subscriptions.unsubscribe(); }
内容的提问来源于stack exchange,提问作者ruddnisrus
相关产品推荐
相关产品推荐

