我创建了这支笔: http://codepen.io/hatelove85911/pen/Vjmwwb
const Ob = Rx.Observable
const button = document.querySelector('#click')
const count$ = Ob.fromEvent(button, 'click')
.scan(acc=>++acc,0)
.share()
setTimeout(()=>{
count$.subscribe(x=>console.log('sub1:',x))
},5000)
setTimeout(()=>{
count$.subscribe(x=>console.log('sub2:',x))
},10000)
所有在线帖子都表示,即使没有订阅者,热观察也会开始发布值。
在这个例子中,我分享了observable以使其变热。 我一直点击按钮几次,期望在第一个订阅者之前获得大于1的累积值,但是当第一个订阅者来时,其日志仍然从1开始,但是,第二个订阅者的日志将从在中间,而不是从1。
有人可以解释为什么会发生这种情况吗?
答案 0 :(得分:4)
热/暖/冷术语总是令人困惑。我试图逃避温度比喻,以确切了解引擎盖下发生的事情。
基本上,所有(Rxjs)可观察量(除了主体)都是懒惰的。这意味着如果没有订户(也称为观察者),则不会有数据流(或任何事实)。您将在此处找到有关订阅时订阅和数据流的说明性和更准确的说明:Hot and Cold observables : are there 'hot' and 'cold' operators?
share
返回一个observable,因此observable也是懒惰的。因此,生产者(由您的运营商链接确定)将在第一次订阅时首次启动。因此,无论您在订阅之前点击多少,都不执行任何操作。订阅一次后,您的生产者将被执行,您的观察者/订阅将生成值1
。
正如您在链接的插图数据流中看到的那样,share
是publish().refCount()
的别名,其中publish
是multicast(new Rx.Subject())
所以Ob.fromEvent(button, 'click').scan(acc=>++acc,0)
有一个主题订阅了它。这里重点是主题实际上没有订阅YET,但是当你打电话给connect()
时。一旦主体订阅了,它就会将它收到的任何值传递给它已经注册的任何观察者此时值到达。这种行为被认为是一种热门行为。令人困惑的部分是热的可观察量是可观察的,因此仍然是懒惰的(除了不懒惰的主题)。
进入细节,publish
返回一个可连接的observable(仍然是懒惰的)。当您订阅它时,您正在订阅上述主题。但只要您不做connect()
,该主题本身并不订阅源可观察对象。因此,没有数据会流动。
为了说服您,请将您的codepen替换为:
const Ob = Rx.Observable
const button = document.querySelector('#click')
const count$ = Ob.fromEvent(button, 'click')
.scan(acc=>++acc,0)
.publish();
setTimeout(()=>{
count$.subscribe(x=>console.log('sub1:',x))
},1000)
setTimeout(()=>{
count$.subscribe(x=>console.log('sub2:',x))
count$.connect();
},5000)
在obs.publish(); obs.subscribe(observer1)
中简而言之,您在连接之前就拥有以下状态obs | subject -> observer1
,其中a-->b
表示b is subscribed to a
。没有数据会流动,因为主题只会传递它收到的值,而且它没有收到任何数据,也没有订阅任何来源。
当您connect()
拥有州:obs -> subject -> observer1
时。因此,obs
生成器将启动,向主体发送值,该主体将值依次发送给接收这些值时的任何观察者。
答案 1 :(得分:1)
我也是RxJS的新手,但我相信fromEvent
更像是一个“温暖”的Observable。基本上,它是懒惰的,因为它不会开始跟踪事情,直到实际的订阅发生在它上面。 share
确实让它变得热门,一旦订阅发生,但由于它的懒惰,在第一个订阅之前没有跟踪任何内容,所以在这个意义上它仍然是一个“温暖”的Observable。
为了让它真正热/非懒,在第一次订阅之前跟踪点击,你可以使用.publish()
(返回一个ConnectableObservables),然后立即.connect()
。
const Ob = Rx.Observable
const button = document.querySelector('#click')
const count$ = Ob.fromEvent(button, 'click')
.scan(acc=>++acc,0)
.publish(); // <-- publish here
count$.connect(); // <--immediately connect to it
setTimeout(()=>{
count$.subscribe(x=>console.log('sub1:',x))
},5000)
setTimeout(()=>{
count$.subscribe(x=>console.log('sub2:',x))
},10000)