「RxJS」全稱 「Reactive Extensions for JavaScript」, RxJS
是一個(gè)庫(kù),它通過使用 observable
序列來(lái)編寫異步和基于事件的程序。本文帶你了解RXJS
中四種Subject
的使用。
介紹
Subject 是用于多播的Observable
,這意味著Subject
確保每個(gè)訂閱都獲得與訂閱者之間共享可觀察執(zhí)行完全相同的值。
在介紹它們之前,我們先來(lái)看一下四種Subject
與普通Observable
的區(qū)別:
Subject
Subject
其實(shí)是觀察者模式的實(shí)現(xiàn),所以當(dāng)觀察者訂閱Subject
對(duì)象時(shí),它會(huì)把訂閱者添加到觀察者列表中,每當(dāng)有接收到新值時(shí),它就會(huì)遍歷觀察者列表,依次調(diào)用觀察者內(nèi)部的next
方法,把值一一送出。我們先來(lái)看一下Subject
的使用方法:
上面例子就比較容易理解:
- 我們創(chuàng)建了一個(gè)
Subject
- 發(fā)出了一個(gè)值1,但由于此時(shí)并沒有訂閱者,所以這個(gè)值不會(huì)被訂閱到
- 創(chuàng)建了訂閱者 A
- 又發(fā)出一個(gè)值 2,這時(shí)候訂閱者 A 會(huì)接收到這個(gè)值
- 又創(chuàng)建一個(gè)訂閱者 B
- 最后發(fā)出一個(gè)值 3,這時(shí)候已經(jīng)訂閱的都會(huì)接收到這個(gè)值
BehaviorSubject
很多時(shí)候我們會(huì)希望Subject
能代表當(dāng)下的狀態(tài),而不是單純的事件發(fā)送,也就是說(shuō)如果當(dāng)前有一個(gè)新的訂閱,我們希望Subject
能立即給出最新的值,而不是沒有回應(yīng)。這個(gè)時(shí)候我們就可以使用到BehaviorSubject
BehaviorSubject
繼承自Subject
,它具有存儲(chǔ)當(dāng)前值的特征。這表示你可以始終直接從BehaviorSubject
獲取到最后發(fā)出的值。請(qǐng)參閱下面代碼示例:
這段代碼做了那些工作呢?
- 我們首先創(chuàng)建了一個(gè)
BehaviorSubject
的實(shí)例behavior$
,并在實(shí)例化時(shí)傳入初始值 0。 - 然后我們訂閱了這個(gè)這個(gè)實(shí)例
behavior$
,由于BehaviorSubject
的特點(diǎn)是把最新的值發(fā)布給訂閱者,訂閱者 A 會(huì)得到初始值 0,所以就會(huì)打引出訂閱者 A,訂閱值為:0 behavior$
使用內(nèi)置的next
方法發(fā)出一個(gè)新的值 1,這時(shí)候訂閱者 A 將會(huì)收到新的值,打印出訂閱者 A,訂閱值為 1- 我們?cè)黾右粋€(gè)訂閱者 B,這時(shí)候它會(huì)得到最新的值 1,所以打印結(jié)果為訂閱者B,訂閱值為 1
- 最后我們?cè)僖淮握{(diào)用
behavior$
的next
方法,由于我們之前已經(jīng)訂閱了兩次,所以訂閱者 A 和訂閱者 B 都會(huì)接收到新的value
(推薦教程:JavaScript教程)
ReplaySubject
有時(shí)候我們創(chuàng)建一個(gè)Subject
,但又想在每次新的訂閱時(shí),它都會(huì)重新發(fā)送最后幾個(gè)值,這個(gè)時(shí)候我們就可以用到ReplaySubject
。
ReplaySubject
可以將舊數(shù)據(jù)發(fā)送給新的訂閱者,這點(diǎn)很像是BehaviorSubject
,但是它還有一個(gè)額外的特性,它可以記錄一部分的observable
執(zhí)行,所以它可以存儲(chǔ)多個(gè)舊值并發(fā)送給它的新訂閱者。
創(chuàng)建ReplaySubject
時(shí),可以指定要存儲(chǔ)多少值以及要存儲(chǔ)多長(zhǎng)時(shí)間。它的第一個(gè)參數(shù) bufferSize
指定了緩存的大小,默認(rèn)為 Infinity
,即緩存所有發(fā)出的值,是一個(gè)「空間限制」。我們還可以向其傳遞第二個(gè)參數(shù) windowTime
,指定緩存的「時(shí)間限制」,默認(rèn)為 Infinity
,即不限制值的失效時(shí)間。請(qǐng)參閱下面代碼示例:
這里發(fā)生了一些事情:
- 我們創(chuàng)建了一個(gè)
ReplaySubject
的實(shí)例replay$
,并指定我們只想存儲(chǔ)最后兩個(gè)值 - 我們創(chuàng)建了一個(gè)訂閱者 A
- 調(diào)用三次
replay$
的next
方法,把值發(fā)布給訂閱者。這時(shí)訂閱者 A 將會(huì)打印三次 - 現(xiàn)在就來(lái)體驗(yàn)
ReplaySubject
的魔力。我們使用replay$
創(chuàng)建了一個(gè)新的訂閱者 B,由于我們告訴ReplaySubject
,存儲(chǔ)兩個(gè)值,因此它將直接向訂閱者 B 發(fā)出這些最后的值,訂閱者 B 將打印出這些值。 replay$
發(fā)出另外一個(gè)值,這時(shí)候,訂閱者 A 和訂閱者 B 都接收到值的改變,打印出另外一個(gè)值
如前面所說(shuō)的一樣,你還可以指定值在ReplaySubject
存儲(chǔ)的時(shí)間,我們來(lái)看一個(gè)例子
上面代碼中發(fā)生了那些事情呢:
- 我們創(chuàng)建了
ReplaySubject
,并指定它只存儲(chǔ)最后兩個(gè)值,但是不超過 100ms - 創(chuàng)建一個(gè)訂閱者 A
- 我們開始每 200ms 發(fā)出一個(gè)新的值。訂閱者 A 會(huì)接收到發(fā)出的所有值
- 我們創(chuàng)建一個(gè)訂閱者 B,由于是在 1000ms 后進(jìn)行訂閱。這意味著在開始訂閱之前,
replay$
已經(jīng)發(fā)出了 5 個(gè)值。在創(chuàng)建ReplaySubject
時(shí),我們指定最多存儲(chǔ) 2 個(gè)值,并且不能超過 100ms。這意味著在 1000ms 后,訂閱者 B 開始訂閱時(shí),由于replay$
是 200ms 發(fā)出一個(gè)值,因此訂閱者 B 只會(huì)接收到 1 個(gè)值。
有的同學(xué)看到這里,會(huì)感覺到ReplaySubject(1)
是不是就等同于BehaviorSubject
。但是,二者無(wú)論在概念上還是行為上,都是有所區(qū)別的。
首先概念上的區(qū)別是本質(zhì)的,ReplaySubject
只是緩存了最近的值,它仍然反映的是不斷有值產(chǎn)生的流(「多值」),而BehaviorSubject
反映的則是隨時(shí)間變化的值(「單值」)。因此,BehaviorSubject
需要傳入一個(gè)初始值,然后這個(gè)值將不斷變化,我們只能看見當(dāng)前的值。
在行為上,由于ReplaySubject
側(cè)重于緩存,那么當(dāng)它完成時(shí),并不會(huì)影響我們繼續(xù)觀測(cè)它緩存的值。我們來(lái)看下面這個(gè)例子:
ReplaySubject
在執(zhí)行完complate
時(shí),我們訂閱它仍然可以拿到緩存的值,而BehaviorSubject
在執(zhí)行完complate
時(shí),我們繼續(xù)訂閱它已經(jīng)沒有任何作用了。
AsyncSubject
雖然BehaviorSubject
和ReplaySubject
都存儲(chǔ)值,但AsyncSubject
的工作方式卻有所不同。AsyncSubject
是一個(gè)Subject
變體,其中僅Observable
執(zhí)行的最后一個(gè)值發(fā)送到其訂閱者,并且僅在執(zhí)行完成時(shí)發(fā)送(類似于rxjs/operators
里面的last
方法)。請(qǐng)參考下面的示例代碼:
這次沒有太多的事情發(fā)生。但是,讓我們看一下執(zhí)行步驟:
- 我們創(chuàng)建
AsyncSubject
的實(shí)例async$
- 我們通過訂閱者 A 進(jìn)行訂閱
async$
發(fā)出 2 個(gè)值,仍然沒有發(fā)生變化- 我們創(chuàng)建一個(gè)訂閱者 B
- 發(fā)出新的值,但是兩個(gè)訂閱者都沒有任何反應(yīng)
async$
執(zhí)行complate
完成,這時(shí)候?qū)⒆詈笠粋€(gè)值發(fā)送給所有訂閱者
從上面的代碼示例可以看出來(lái)AsyncSubject
會(huì)在執(zhí)行complate
后才送出最后一個(gè)值,其實(shí)這個(gè)行為跟 Promise
很像,都是等到事情結(jié)束后送出一個(gè)值。在 Promise
中,我們可以通過 resolve(value)
聲明任務(wù)完成,并將獲得的值發(fā)送出去,然后再通過Promise.then()
方法中處理得到的值。
(推薦微課:JavaScript微課)
以上就是關(guān)于RXJS
中四種Subject
的區(qū)別的相關(guān)介紹了,希望對(duì)大家有所幫助。