RxJS中switchMap结合combineLatest触发两次请求?如何解决?
解决方案:避免Angular中Observable重复订阅导致的重复HTTP请求
问题根源
你的场景中,files$是基于sessionInfo$通过switchMap生成的冷Observable(HTTP请求属于冷Observable,每次订阅都会重新执行),而isAdditionalInfoAvailable$被模板中的async管道订阅了两次,导致files$被触发两次,进而发起两次HTTP请求。
方案1:给files$添加shareReplay(1)
通过shareReplay(1)缓存files$的最新请求结果,新订阅会复用缓存值,同时当sessionInfo$更新时,switchMap仍会正常触发新请求并更新缓存。
// 组件TS代码示例 sessionInfo$ = new BehaviorSubject<SessionInfo>(/* 填入初始值,比如默认SessionInfo对象或null */); files$ = this.sessionInfo$.pipe( // 过滤无效的sessionInfo,避免发起无效请求 filter(sessionInfo => !!sessionInfo), switchMap(sessionInfo => { // 这里能正常获取sessionInfo,因为switchMap在sessionInfo$发射有效值时执行 return this.filesApiService.getPublicDocuments(sessionInfo.id); }), shareReplay(1) // 缓存最新请求结果,复用给后续订阅 ); isAdditionalInfoAvailable$ = combineLatest([this.sessionInfo$, this.files$]).pipe( map(([sessionInfo, files]) => { // 自定义判断逻辑,比如文件数量大于0或结合sessionInfo的条件 return files.length > 0; }) );
方案2:直接给isAdditionalInfoAvailable$添加shareReplay(1)
如果不需要单独复用files$,可以直接在最终的组合Observable上添加缓存,让所有订阅共享同一执行链,从根源避免重复订阅。
// 组件TS代码示例 sessionInfo$ = new BehaviorSubject<SessionInfo>(/* 初始值 */); files$ = this.sessionInfo$.pipe( filter(sessionInfo => !!sessionInfo), switchMap(sessionInfo => this.filesApiService.getPublicDocuments(sessionInfo.id)) ); isAdditionalInfoAvailable$ = combineLatest([this.sessionInfo$, this.files$]).pipe( map(([sessionInfo, files]) => files.length > 0), shareReplay(1) // 让多次订阅共享同一执行结果 );
为什么之前用share无效?
share默认在所有订阅取消后会重置缓存,且仅共享同一生命周期内的订阅。而shareReplay(1)会持久化缓存最新值,无论订阅数量变化都能复用,更适合HTTP请求这类需要缓存结果的场景。此外,如果之前仅给HTTP请求返回的Observable加share,每次switchMap生成的都是新Observable,无法跨switchMap调用共享结果,所以无效。
内容的提问来源于stack exchange,提问作者Peter Key
相关产品推荐
相关产品推荐

