Angular中订阅BehaviorSubject的asObservable为何不触发onComplete?
问题分析与解决方案
你遇到的核心问题是BehaviorSubject的特性导致其对应的Observable不会自动完成:
BehaviorSubject属于RxJS里的「持续型多播Observable」,它会一直处于活跃状态,用来持续推送新的值,除非你手动调用它的complete()方法。而HttpClient返回的Observable是「单次型Observable」,请求完成后就会自动触发complete,所以那边的回调能正常执行。
知道原因后,有几种方案可以解决你的需求:
方案1:只获取当前值,后续不需要更新(最适合你的场景)
如果你只是想获取当前localStorage里的subModuleId,不需要监听后续的变化,可以用take(1)操作符,让Observable在发出第一个值后立即完成:
修改服务的getModuleId方法:
import { Injectable} from "@angular/core"; import { BehaviorSubject } from 'rxjs'; import { take } from 'rxjs/operators'; // 引入take操作符 @Injectable() export class NavigationStorageService { private ModuleTitle = new BehaviorSubject<string>(""); // 建议改成私有变量,避免外部直接操作 currentModuleTitle = this.ModuleTitle.asObservable(); constructor() {} setModuleId(ModuleId: string) { localStorage.setItem('subModuleId', ModuleId); this.ModuleTitle.next(ModuleId); } getModuleId() { this.ModuleTitle.next(localStorage.getItem('subModuleId')); return this.currentModuleTitle.pipe(take(1)); // 添加take(1) } }
或者在订阅时添加take(1):
getModuleId() { this._navigationStorageService.getModuleId() .pipe(take(1)) .subscribe( subModuleId => { this.subModuleId = subModuleId }, error => { console.log(error) }, () => console.log("getModuleId") // 现在会正常触发了 ); }
这样修改后,Observable在发出第一个值后就会自动complete,你的complete回调和finally()都会正常执行。
方案2:手动触发complete(适合不再需要该流的场景)
如果你确实需要监听后续的更新,但在某个特定时机希望流结束,可以在服务里手动调用this.ModuleTitle.complete(),不过注意:一旦调用complete,这个BehaviorSubject就再也无法推送新值了,所以只适合确定不再使用该流的场景。
补充说明:first() vs take(1)
你也可以用first()操作符,它和take(1)类似,但first()会要求流至少发出一个值,如果流为空会抛出错误;而take(1)如果流为空会直接complete。根据你的场景,用take(1)更稳妥。
内容的提问来源于stack exchange,提问作者Adam Niedziałek
相关产品推荐
相关产品推荐

