App下載

了解RXJS中四種Subject的區(qū)別

猿友 2020-08-17 16:35:48 瀏覽數(shù) (4145)
反饋

「RxJS」全稱 「Reactive Extensions for JavaScript」, RxJS 是一個(gè)庫,它通過使用 observable 序列來編寫異步和基于事件的程序。本文帶你了解RXJS中四種Subject的使用。

介紹

Subject 是用于多播的Observable,這意味著Subject確保每個(gè)訂閱都獲得與訂閱者之間共享可觀察執(zhí)行完全相同的值。

在介紹它們之前,我們先來看一下四種Subject與普通Observable的區(qū)別:

區(qū)別

Subject

Subject其實(shí)是觀察者模式的實(shí)現(xiàn),所以當(dāng)觀察者訂閱Subject對象時(shí),它會把訂閱者添加到觀察者列表中,每當(dāng)有接收到新值時(shí),它就會遍歷觀察者列表,依次調(diào)用觀察者內(nèi)部的next方法,把值一一送出。我們先來看一下Subject的使用方法:

Subject的使用方法

上面例子就比較容易理解:

  1. 我們創(chuàng)建了一個(gè)Subject
  2. 發(fā)出了一個(gè)值1,但由于此時(shí)并沒有訂閱者,所以這個(gè)值不會被訂閱到
  3. 創(chuàng)建了訂閱者 A
  4. 又發(fā)出一個(gè)值 2,這時(shí)候訂閱者 A 會接收到這個(gè)值
  5. 又創(chuàng)建一個(gè)訂閱者 B
  6. 最后發(fā)出一個(gè)值 3,這時(shí)候已經(jīng)訂閱的都會接收到這個(gè)值

BehaviorSubject

很多時(shí)候我們會希望Subject能代表當(dāng)下的狀態(tài),而不是單純的事件發(fā)送,也就是說如果當(dāng)前有一個(gè)新的訂閱,我們希望Subject能立即給出最新的值,而不是沒有回應(yīng)。這個(gè)時(shí)候我們就可以使用到BehaviorSubject

BehaviorSubject繼承自Subject,它具有存儲當(dāng)前值的特征。這表示你可以始終直接從BehaviorSubject獲取到最后發(fā)出的值。請參閱下面代碼示例:

BehaviorSubject

這段代碼做了那些工作呢?

  1. 我們首先創(chuàng)建了一個(gè)BehaviorSubject的實(shí)例behavior$,并在實(shí)例化時(shí)傳入初始值 0。
  2. 然后我們訂閱了這個(gè)這個(gè)實(shí)例behavior$,由于BehaviorSubject的特點(diǎn)是把最新的值發(fā)布給訂閱者,訂閱者 A 會得到初始值 0,所以就會打引出訂閱者 A,訂閱值為:0
  3. behavior$使用內(nèi)置的next方法發(fā)出一個(gè)新的值 1,這時(shí)候訂閱者 A 將會收到新的值,打印出訂閱者 A,訂閱值為 1
  4. 我們增加一個(gè)訂閱者 B,這時(shí)候它會得到最新的值 1,所以打印結(jié)果為訂閱者B,訂閱值為 1
  5. 最后我們再一次調(diào)用behavior$next方法,由于我們之前已經(jīng)訂閱了兩次,所以訂閱者 A 和訂閱者 B 都會接收到新的value

(推薦教程:JavaScript教程

ReplaySubject

有時(shí)候我們創(chuàng)建一個(gè)Subject,但又想在每次新的訂閱時(shí),它都會重新發(fā)送最后幾個(gè)值,這個(gè)時(shí)候我們就可以用到ReplaySubject

ReplaySubject可以將舊數(shù)據(jù)發(fā)送給新的訂閱者,這點(diǎn)很像是BehaviorSubject,但是它還有一個(gè)額外的特性,它可以記錄一部分的observable執(zhí)行,所以它可以存儲多個(gè)舊值并發(fā)送給它的新訂閱者。

創(chuàng)建ReplaySubject時(shí),可以指定要存儲多少值以及要存儲多長時(shí)間。它的第一個(gè)參數(shù) bufferSize指定了緩存的大小,默認(rèn)為 Infinity,即緩存所有發(fā)出的值,是一個(gè)「空間限制」。我們還可以向其傳遞第二個(gè)參數(shù) windowTime,指定緩存的「時(shí)間限制」,默認(rèn)為 Infinity,即不限制值的失效時(shí)間。請參閱下面代碼示例:

ReplaySubject

這里發(fā)生了一些事情:

  1. 我們創(chuàng)建了一個(gè)ReplaySubject的實(shí)例replay$,并指定我們只想存儲最后兩個(gè)值
  2. 我們創(chuàng)建了一個(gè)訂閱者 A
  3. 調(diào)用三次replay$next方法,把值發(fā)布給訂閱者。這時(shí)訂閱者 A 將會打印三次
  4. 現(xiàn)在就來體驗(yàn)ReplaySubject的魔力。我們使用replay$創(chuàng)建了一個(gè)新的訂閱者 B,由于我們告訴ReplaySubject,存儲兩個(gè)值,因此它將直接向訂閱者 B 發(fā)出這些最后的值,訂閱者 B 將打印出這些值。
  5. replay$發(fā)出另外一個(gè)值,這時(shí)候,訂閱者 A 和訂閱者 B 都接收到值的改變,打印出另外一個(gè)值

如前面所說的一樣,你還可以指定值在ReplaySubject存儲的時(shí)間,我們來看一個(gè)例子

ReplaySubject

上面代碼中發(fā)生了那些事情呢:

  1. 我們創(chuàng)建了ReplaySubject,并指定它只存儲最后兩個(gè)值,但是不超過 100ms
  2. 創(chuàng)建一個(gè)訂閱者 A
  3. 我們開始每 200ms 發(fā)出一個(gè)新的值。訂閱者 A 會接收到發(fā)出的所有值
  4. 我們創(chuàng)建一個(gè)訂閱者 B,由于是在 1000ms 后進(jìn)行訂閱。這意味著在開始訂閱之前,replay$已經(jīng)發(fā)出了 5 個(gè)值。在創(chuàng)建ReplaySubject時(shí),我們指定最多存儲 2 個(gè)值,并且不能超過 100ms。這意味著在 1000ms 后,訂閱者 B 開始訂閱時(shí),由于replay$是 200ms 發(fā)出一個(gè)值,因此訂閱者 B 只會接收到 1 個(gè)值。

有的同學(xué)看到這里,會感覺到ReplaySubject(1)是不是就等同于BehaviorSubject。但是,二者無論在概念上還是行為上,都是有所區(qū)別的。

首先概念上的區(qū)別是本質(zhì)的,ReplaySubject只是緩存了最近的值,它仍然反映的是不斷有值產(chǎn)生的流(「多值」),而BehaviorSubject反映的則是隨時(shí)間變化的值(「單值」)。因此,BehaviorSubject需要傳入一個(gè)初始值,然后這個(gè)值將不斷變化,我們只能看見當(dāng)前的值。

在行為上,由于ReplaySubject側(cè)重于緩存,那么當(dāng)它完成時(shí),并不會影響我們繼續(xù)觀測它緩存的值。我們來看下面這個(gè)例子:

ReplaySubject

ReplaySubject在執(zhí)行完complate時(shí),我們訂閱它仍然可以拿到緩存的值,而BehaviorSubject在執(zhí)行完complate時(shí),我們繼續(xù)訂閱它已經(jīng)沒有任何作用了。

AsyncSubject

雖然BehaviorSubjectReplaySubject都存儲值,但AsyncSubject的工作方式卻有所不同。AsyncSubject是一個(gè)Subject變體,其中僅Observable執(zhí)行的最后一個(gè)值發(fā)送到其訂閱者,并且僅在執(zhí)行完成時(shí)發(fā)送(類似于rxjs/operators里面的last方法)。請參考下面的示例代碼:

AsyncSubject

這次沒有太多的事情發(fā)生。但是,讓我們看一下執(zhí)行步驟:

  1. 我們創(chuàng)建AsyncSubject的實(shí)例async$
  2. 我們通過訂閱者 A 進(jìn)行訂閱
  3. async$發(fā)出 2 個(gè)值,仍然沒有發(fā)生變化
  4. 我們創(chuàng)建一個(gè)訂閱者 B
  5. 發(fā)出新的值,但是兩個(gè)訂閱者都沒有任何反應(yīng)
  6. async$執(zhí)行complate完成,這時(shí)候?qū)⒆詈笠粋€(gè)值發(fā)送給所有訂閱者

從上面的代碼示例可以看出來AsyncSubject會在執(zhí)行complate后才送出最后一個(gè)值,其實(shí)這個(gè)行為跟 Promise 很像,都是等到事情結(jié)束后送出一個(gè)值。在 Promise 中,我們可以通過 resolve(value)聲明任務(wù)完成,并將獲得的值發(fā)送出去,然后再通過Promise.then()方法中處理得到的值。

(推薦微課:JavaScript微課

以上就是關(guān)于RXJS中四種Subject的區(qū)別的相關(guān)介紹了,希望對大家有所幫助。

0 人點(diǎn)贊