为何RxJS的share操作符对range和timer创建的Observable表现不同?
这是个很典型的RxJS冷/热Observable细节问题,核心在于同步Observable和异步Observable在share的refCount机制下的行为差异,咱们一步步拆解:
先搞懂share的本质
share()其实是multicast(() => new Subject()), refCount()的语法糖,它的核心逻辑是:
- 当第一个订阅者出现时(订阅数从0→1),自动连接源Observable
- 当最后一个订阅者取消订阅时(订阅数从1→0),自动断开与源Observable的连接
分析range的情况
你用的range(1,1)是同步且立即完成的冷Observable:
- 当第一个订阅
subscribeThree执行时,share的refCount从0变为1,触发源Observable的订阅 range同步发出值、执行tap的副作用、完成整个流,这时候subscribeThree的订阅也跟着完成了- 订阅数回到0,
share断开了与源的连接 - 当第二个订阅
subscribeFour执行时,refCount又从0变为1,再次触发源Observable的订阅,于是tap的副作用又执行了一遍,结果就是你看到的两次副作用输出
对比timer的情况
timer是异步的冷Observable:
- 第一个订阅执行时,
refCount到1,连接源,timer开始计时,但不会立即完成 - 在
timer触发值之前,第二个订阅已经执行,refCount变为2 - 当
timer发出值时,两个订阅都会收到这个值,同时源完成,订阅数回到0 - 整个过程中源只被执行了一次,所以副作用只会触发一次
怎么让range也能共享副作用?
如果想让range的share表现得和timer类似,你可以把它改成异步的,比如用asyncScheduler:
const source = range(1, 1, asyncScheduler) .pipe( share() )
这样range会在异步队列里执行,第一个订阅后不会立即完成,第二个订阅能在源完成前加入,共享同一个源执行,副作用就只会触发一次。
另外,如果你希望无论订阅时机如何,都能共享之前的执行结果,可以用shareReplay(),它会缓存源的输出,后续订阅直接拿到缓存值,不会重新触发源。
内容的提问来源于stack exchange,提问作者ps-aux
相关产品推荐
相关产品推荐

