瞭解RXJS的 Subject,BehaviorSubject,AsyncSubject,ReplaySubject用法

RxJS全稱 Reactive Extensions for JavaScript, RxJS 是一個庫,它經過使用 observable 序列來編寫異步和基於事件的程序。緩存

Photo by Jasper Boer on Unsplashmarkdown

下面帶你瞭解 RXJS 中四種Subject的使用。併發

介紹

Subject 是是用於多播的Observable,這意味着Subject確保每一個訂閱都得到與訂閱者之間共享可觀察執行徹底相同的值。異步

在介紹它們以前,咱們先來看一下四種Subject與普通Observable的區別:async

Observable Subject BehaviorSubject ReplaySubject AsyncSubject
數據生成者 數據生成者和消費者 數據生成者和消費者 數據生成者和消費者 數據生成者和消費者
單播 多播 多播 多播 多播
每一個訂閱都會產生一個新的實例,每一個訂閱都是從第一個產生的值開始接收值,因此每一個訂閱接收到的值都是同樣的 將值多播給已經註冊監聽該 Subject 的觀察者們 當即把最新的一個值發送給新的觀察者(實例化時須要初始值) 能夠存儲多箇舊值並將它們重播給新的訂閱者 把執行 complate 方法後的最後一個值發送給全部訂閱者

Subject

Subject實際上是觀察者模式的實現,因此當觀察者訂閱Subject對象時,它會把訂閱者添加到觀察者列表中,每當有接收到新值時,它就會遍歷觀察者列表,依次調用觀察者內部的next方法,把值一一送出。咱們先來看一下Subject的使用方法:oop

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

  1. 咱們建立了一個Subject
  2. 發出了一個值1,但因爲此時並無訂閱者,因此這個值不會被訂閱到
  3. 建立了訂閱者 A
  4. 又發出一個值2,這時候訂閱者 A 會接收到這個值
  5. 又建立一個訂閱者 B
  6. 最後發出一個值3,這時候已經訂閱的都會接收到這個值

BehaviorSubject

不少時候咱們會但願Subject能表明當下的狀態,而不是單純的事件發送,也就是說若是當前有一個新的訂閱,咱們但願Subject能當即給出最新的值,而不是沒有迴應。這個時候咱們就可使用到BehaviorSubjectcode

BehaviorSubject繼承自Subject,它具備存儲當前值的特徵。這表示你能夠始終直接從BehaviorSubject獲取到最後發出的值。請參閱下面代碼示例:orm

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

  1. 咱們首先建立了一個BehaviorSubject的實例behavior$,並在實例化時傳入初始值0
  2. 而後咱們訂閱了這個這個實例behavior$,因爲BehaviorSubject的特色是把最新的值發佈給訂閱者,訂閱者 A 會獲得初始值0,因此就會打引出訂閱者A,訂閱值爲:0
  3. behavior$使用內置的next方法發出一個新的值1,這時候訂閱者 A 將會收到新的值,打印出訂閱者A,訂閱值爲1
  4. 咱們增長一個訂閱者 B,這時候它會獲得最新的值1,因此打印結果爲訂閱者B,訂閱值爲1
  5. 最後咱們再一次調用behavior$next方法,因爲咱們以前已經訂閱了兩次,因此訂閱者 A 和訂閱者 B 都會接收到新的value

ReplaySubject

有時候咱們建立一個Subject,但又想在每次新的訂閱時,它都會從新發送最後幾個值,這個時候咱們就能夠用到ReplaySubject

ReplaySubject能夠將舊數據發送給新的訂閱者,這點很像是BehaviorSubject,可是它還有一個額外的特性,它能夠記錄一部分的observable執行,因此它能夠存儲多箇舊值併發送給它的新訂閱者。

建立ReplaySubject時,能夠指定要存儲多少值以及要存儲多長時間。它的第一個參數 bufferSize指定了緩存的大小,默認爲 Infinity,即緩存全部發出的值,是一個空間限制。咱們還能夠向其傳遞第二個參數 windowTime,指定緩存的時間限制,默認爲 Infinity,即不限制值的失效時間。請參閱下面代碼示例:

這裏發生了一些事情:

  1. 咱們建立了一個ReplaySubject的實例replay$,並指定咱們只想存儲最後兩個值
  2. 咱們建立了一個訂閱者 A
  3. 調用三次replay$next方法,把值發佈給訂閱者。這時訂閱者 A 將會打印三次
  4. 如今就來體驗ReplaySubject的魔力。咱們使用replay$建立了一個新的訂閱者 B,因爲咱們告訴ReplaySubject,存儲兩個值,所以它將直接向訂閱者 B 發出這些最後的值,訂閱者 B 將打印出這些值。
  5. replay$發出另一個值,這時候,訂閱者 A 和訂閱者 B 都接收到值的改變,打印出另一個值

如前面所說的同樣,你還能夠指定值在ReplaySubject存儲的時間,咱們來看一個例子

上面代碼中發生了那些事情呢:

  1. 咱們建立了ReplaySubject,並指定它只存儲最後兩個值,可是不超過 100ms
  2. 建立一個訂閱者 A
  3. 咱們開始每 200ms 發出一個新的值。訂閱者 A 會接收到發出的全部值
  4. 咱們建立一個訂閱者 B,因爲是在 1000ms 後進行訂閱。這意味着在開始訂閱以前,replay$已經發出了 5 個值。在建立ReplaySubject時,咱們指定最多存儲 2 個值,而且不能超過 100ms。這意味着在 1000ms 後,訂閱者 B 開始訂閱時,因爲replay$是 200ms 發出一個值,所以訂閱者 B 只會接收到 1 個值。

有的同窗看到這裏,會感受到ReplaySubject(1)是否是就等同於BehaviorSubject。可是,兩者不管在概念上仍是行爲上,都是有所區別的。

首先概念上的區別是本質的,ReplaySubject只是緩存了最近的值,它仍然反映的是不斷有值產生的流(多值),而BehaviorSubject反映的則是隨時間變化的值(單值)。所以,BehaviorSubject須要傳入一個初始值,而後這個值將不斷變化,咱們只能看見當前的值。

在行爲上,因爲ReplaySubject側重於緩存,那麼當它完成時,並不會影響咱們繼續觀測它緩存的值。咱們來看下面這個例子:

ReplaySubject在執行完complate時,咱們訂閱它仍然能夠拿到緩存的值,而BehaviorSubject在執行完complate時,咱們繼續訂閱它已經沒有任何做用了。

AsyncSubject

雖然BehaviorSubjectReplaySubject都存儲值,但AsyncSubject的工做方式卻有所不一樣。AsyncSubject是一個Subject變體,其中僅Observable執行的最後一個值發送到其訂閱者,而且僅在執行完成時發送(相似於rxjs/operators裏面的last方法)。請參考下面的示例代碼:

此次沒有太多的事情發生。可是,讓咱們看一下執行步驟:

  1. 咱們建立AsyncSubject的實例async$
  2. 咱們經過訂閱者 A 進行訂閱
  3. async$發出 2 個值,仍然沒有發生變化
  4. 咱們建立一個訂閱者 B
  5. 發出新的值,可是兩個訂閱者都沒有任何反應
  6. async$執行complate完成,這時候將最後一個值發送給全部訂閱者

從上面的代碼示例能夠看出來AsyncSubject會在執行complate後才送出最後一個值,其實這個行爲跟 Promise 很像,都是等到事情結束後送出一個值。在 Promise 中,咱們能夠經過 resolve(value)聲明任務完成,並將得到的值發送出去,而後再經過Promise.then()方法中處理獲得的值。

總結

本文介紹了Subject的一些應用方式,以及BehaviorSubjectReplaySubjectAsyncSubject三個變形各自的特性介紹。不知道你是否有收穫呢,若是喜歡就點贊關注吧。

相關文章
相關標籤/搜索