App下載

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

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

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

介紹

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

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

區(qū)別

Subject

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

Subject的使用方法

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

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

BehaviorSubject

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

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

BehaviorSubject

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

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

(推薦教程:JavaScript教程

ReplaySubject

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

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

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

ReplaySubject

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

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

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

ReplaySubject

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

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

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

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

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

ReplaySubject

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

AsyncSubject

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

AsyncSubject

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

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

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

(推薦微課:JavaScript微課

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

0 人點贊