You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

fromFetch操作符取消订阅后未重置缓存的问题排查

问题:RxJS shareReplay(refCount: true) 搭配 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 16:02:50