fromFetch操作符取消订阅后未重置缓存的问题排查
我用两段代码测试 RxJS 的 shareReplay({ bufferSize: 1, refCount: true }) 行为:
测试代码1(符合预期)
const test = timer(0, 1000).pipe(shareReplay({ bufferSize: 1, refCount: true })); const subscription1 = test.subscribe( (value) => console.log('Subscription 1:', value), (error) => console.error(error), () => console.log('Subscription 1 completed'), ); setTimeout(() => { subscription1.unsubscribe(); }, 6000); setTimeout(() => { const subscription2 = test.subscribe( (value) => console.log('Subscription 2:', value), (error) => console.error(error), () => console.log('Subscription 2 completed'), ); }, 10000);
这段代码的行为符合预期:subscription1 订阅后,Observable 发射5次值,6秒后取消订阅,此时 test 的 refCount 回到0,缓存被清除。10秒后 subscription2 订阅时,会从0开始重新发射值。
测试代码2(不符合预期)
但用 fromFetch 操作符时,即使没有活跃订阅者,缓存也没被清除:
export const groupsRaw$ = fromFetch('/some_endpoint').pipe( shareReplay({ bufferSize: 1, refCount: true }), ); const subscription1 = groupsRaw$.subscribe( (value) => console.log('Subscription 1:', value), (error) => console.error(error), () => console.log('Subscription 1 completed'), ); setTimeout(() => { subscription1.unsubscribe(); }, 6000); setTimeout(() => { const subscription2 = groupsRaw$.subscribe( (value) => console.log('Subscription 2:', value), (error) => console.error(error), () => console.log('Subscription 2 completed'), ); }, 10000);
网络面板只显示一次请求,我预期 subscription2 订阅时会重新发起请求,但它却读取了缓存,请问我忽略了什么?
解答
核心原因是 shareReplay({ refCount: true }) 的缓存清除逻辑只在源 Observable 未完成时生效,而 fromFetch 返回的 Observable 特性和 timer 完全不同:
- timer 的特性:
timer(0, 1000)是一个持续活跃的无限 Observable,当你取消subscription1时,源 Observable 还在运行(没有完成)。此时refCount降到0,shareReplay会断开与源的连接,同时清除缓存。后续新订阅会重新订阅源 Observable,从头开始发射值。 - fromFetch 的特性:
fromFetch返回的 Observable 是一次性的——只要请求完成(成功或失败),源 Observable 就会进入完成状态。在你的代码里,fetch 请求肯定在6秒内就完成了,所以当你取消subscription1时,源 Observable 已经完成了。
对于已经完成的源 Observable,shareReplay({ refCount: true }) 会永久保留缓存,因为 refCount 的逻辑只负责管理活跃订阅与源 Observable 的连接,当源已经结束,缓存就不会再被清除。这就是为什么 subscription2 订阅时直接读取了缓存,没有重新发起请求。
如果想要实现“无活跃订阅时清除缓存,下次订阅重新发起请求”的效果,可以用 share 搭配 replay,或者手动管理缓存的生命周期,比如用 takeUntil 结合一个信号来重置缓存。
内容的提问来源于stack exchange,提问作者jalane
相关产品推荐
相关产品推荐

