RxJS的share操作符在Observable完成时行为异常的疑问
嘿,这个问题确实戳中了很多人对RxJS share()操作符的理解误区,我来一步步帮你拆解清楚~
首先得明确一个核心点:share()其实是publish().refCount()的语法糖,我们得先搞懂这两个操作符组合起来的行为逻辑,才能明白你代码里发生的事。
你的示例代码里,源Observable是Rx.Observable.of(1),这是一个典型的冷Observable——冷Observable的特点是每次被订阅时,都会从头完整执行一遍,发射所有值然后完成。
现在我们一步步走你的代码流程:
- 第一次订阅
source:refCount从0变成1,触发publish()创建的ConnectableObservable去连接源Observable。源发射1,然后完成。此时因为源完成了,refCount自动降到0。 - 第二次订阅
source:refCount再次从0变成1,这时候关键来了——因为之前的源已经完成了,ConnectableObservable会重新去订阅源Observable。而因为源是冷的,重新订阅就意味着源会再次执行一遍,发射1然后完成,所以第二次订阅也能打印出1。
你可能会疑惑:那refCount的作用不是当计数归0时停止源吗?没错,但那是针对未完成的源来说的。如果源已经自己完成了,那当新的订阅进来时,share()会直接重新触发源的订阅流程,而不是复用之前的源实例。
我们可以加个副作用来验证这个逻辑,比如给源Observable加个打印:
const source = Rx.Observable.create(observer => { console.log('源Observable开始执行'); observer.next(1); observer.complete(); }).share(); source.subscribe(console.log); // 输出: // 源Observable开始执行 // 1 source.subscribe(console.log); // 输出: // 源Observable开始执行 // 1
你看,每次订阅都会触发源重新执行,这就解释了为什么两次都能拿到值。
如果换一个不会自动完成的源,比如interval,情况就不一样了:
const source = Rx.Observable.interval(1000).share(); const sub1 = source.subscribe(v => console.log('订阅1:', v)); // 1秒后打印"订阅1: 0",2秒后"订阅1: 1"... setTimeout(() => { const sub2 = source.subscribe(v => console.log('订阅2:', v)); // 此时refCount变成2,两个订阅都会拿到后续的值 setTimeout(() => { sub1.unsubscribe(); sub2.unsubscribe(); // refCount归0,源停止发射 setTimeout(() => { source.subscribe(v => console.log('订阅3:', v)); // refCount回到1,源重新启动,从0开始发射 }, 1500); }, 2000); }, 2000);
这个例子里,当所有订阅取消后源停止了,新订阅会重新启动源,但因为源是interval,所以会发射新的序列,而不是之前的旧值。
总结一下关键点:
share()不会缓存源发射的值,它只是在有活跃订阅时共享同一个源实例- 当源Observable完成后,后续的订阅会触发重新订阅源,冷Observable每次订阅都会重新发射所有值
- 如果需要缓存值给后续订阅,可以用
shareReplay(),它会缓存指定数量的值,即使源完成了也能提供给新订阅
内容的提问来源于stack exchange,提问作者Royi Namir
相关产品推荐
相关产品推荐

