[譯] 認識 rxjs 中的 BehaviorSubject、ReplaySubject 以及 AsyncSubject

原文連接:Understanding rxjs BehaviorSubject, ReplaySubject and AsyncSubject
原文做者:Luuk Gruijs;發表於2018年5月4日
譯者:yk;如需轉載,請註明出處,謝謝合做!javascript

Subject 的做用是實現 Observable 的多播。因爲其 Observable execution 是在多個訂閱者之間共享的,因此它能夠確保每一個訂閱者接收到的數據絕對相等。不只使用 Subject 能夠實現多播,RxJS 還提供了一些 Subject 的變體以應對不一樣場景,那就是:BehaviorSubject、ReplaySubject 以及 AsyncSubject。java

若是你還不知道 Subject(主題)是什麼的話,建議先讀讀個人上一篇文章:認識 rxjs 中的 Subject。若是你以爲 OK 沒問題,那咱繼續!git

攝影:Cory Schadt,來自 Unsplashgithub

BehaviorSubject

BehaviorSubject 是 Subject 的變體之一。BehaviorSubject 的特性就是它會存儲「當前」的值。這意味着你始終能夠直接拿到 BehaviorSubject 最後一次發出的值。api

有兩種方法能夠拿到 BehaviorSubject 「當前」的值:訪問其 .value 屬性或者直接訂閱。若是你選擇了訂閱,那麼 BehaviorSubject 將直接給訂閱者發送當前存儲的值,不管這個值有多麼「久遠」。請看下面的例子:app

import * as Rx from "rxjs";

const subject = new Rx.BehaviorSubject(Math.random());

// 訂閱者 A
subject.subscribe((data) => {
  console.log('Subscriber A:', data);
});

subject.next(Math.random());

// 訂閱者 B
subject.subscribe((data) => {
  console.log('Subscriber B:', data);
});

subject.next(Math.random());

console.log(subject.value)

// 輸出
// Subscriber A: 0.24957144215097515
// Subscriber A: 0.8751123892486292
// Subscriber B: 0.8751123892486292
// Subscriber A: 0.1901322109907977
// Subscriber B: 0.1901322109907977
// 0.1901322109907977
複製代碼

詳細講解一下:dom

  1. 咱們首先建立了一個 subject,並在其中註冊了一個訂閱者A。因爲 subject 是一個 BehaviorSubject,因此訂閱者A 會當即收到 subject 的初始值(一個隨機數),並同時將其打印。
  2. subject 隨即廣播下一個值。訂閱者A 再次打印收到的值。
  3. 第二次訂閱 subject 並稱其爲訂閱者B。一樣,訂閱者B 也會當即收到 subject 當前存儲的值並打印。
  4. subject 再次廣播下一個新的值。這時,兩個訂閱者都會收到這個值並打印。
  5. 最後,咱們經過簡單地訪問 .value 屬性的形式獲取了 subject 當前的值並打印。這在同步的場景下很是好用,由於你沒必要訂閱 Subject 就能夠獲取它的值。

另外,你可能發現了 BehaviorSubject 在建立時是須要設置一個初始值的。這一點在 Observable 上就很是難實現,而在 BehaviorSubject 上,只要傳遞一個值就好了。async

譯者注:當前版本的 RxJS 中 BehaviorSubject() 是必需要設置初始值的,不然會致使執行錯誤,而原文並無體現這一點。因此我在這一段作了較多改動,以避免誤導讀者。詳見 BehaviorSubjectpost

ReplaySubject

相比 BehaviorSubject 而言,ReplaySubject 是能夠給新訂閱者發送「舊」數據的。另外,ReplaySubject 還有一個額外的特性就是它能夠記錄一部分的 observable execution,從而存儲一些舊的數據用來「重播」給新來的訂閱者。ui

當建立 ReplaySubject 時,你能夠指定存儲的數據量以及數據的過時時間。也就是說,你能夠實現:給新來的訂閱者「重播」訂閱前一秒內的最後五個已廣播的值。示例代碼以下:

import * as Rx from "rxjs";

const subject = new Rx.ReplaySubject(2);

// 訂閱者 A
subject.subscribe((data) => {
  console.log('Subscriber A:', data);
});

subject.next(Math.random())
subject.next(Math.random())
subject.next(Math.random())

// 訂閱者 B
subject.subscribe((data) => {
  console.log('Subscriber B:', data);
});

subject.next(Math.random());

// Subscriber A: 0.3541746356538569
// Subscriber A: 0.12137498878080955
// Subscriber A: 0.531935186034298
// Subscriber B: 0.12137498878080955
// Subscriber B: 0.531935186034298
// Subscriber A: 0.6664809293975393
// Subscriber B: 0.6664809293975393
複製代碼

簡單解讀一下代碼:

  1. 咱們建立了一個 ReplaySubject 並指定其只存儲最近兩次廣播的值;
  2. 訂閱 subject 並稱其爲訂閱者A;
  3. subject 連續廣播三次,同時訂閱者A 也會跟着連續打印三次;
  4. 這一步就輪到 ReplaySubject 展示魔力了。咱們再次訂閱 subject 並稱其爲訂閱者B,由於以前咱們指定 subject 存儲最近兩次廣播的值,因此 subject 會將上兩個值「重播」給訂閱者B。咱們能夠看到訂閱者B 隨即打印了這兩個值;
  5. subject 最後一次廣播,兩個訂閱者收到值並打印。

以前提到了你還能夠設置 ReplaySubject 的數據過時時間。讓咱們來看看下面這個例子:

import * as Rx from "rxjs";

const subject = new Rx.ReplaySubject(2, 100);

// 訂閱者A
subject.subscribe((data) => {
  console.log('Subscriber A:', data);
});

setInterval(() => subject.next(Math.random()), 200);

// 訂閱者B
setTimeout(() => {
  subject.subscribe((data) => {
    console.log('Subscriber B:', data);
  });
}, 1000)

// Subscriber A: 0.44524184251927656
// Subscriber A: 0.5802631630066313
// Subscriber A: 0.9792165506699135
// Subscriber A: 0.3239616040117268
// Subscriber A: 0.6845077617520203
// Subscriber B: 0.6845077617520203
// Subscriber A: 0.41269171141525707
// Subscriber B: 0.41269171141525707
// Subscriber A: 0.8211466186035139
// Subscriber B: 0.8211466186035139
複製代碼

一樣解讀一下代碼:

  1. 咱們建立了一個 ReplaySubject,指定其存儲最近兩次廣播的值,但只保留 100ms;
  2. 訂閱 subject 並稱其爲訂閱者A;
  3. 咱們讓這個 subject 每 200ms 廣播一次。訂閱者A 每次都會收到值並打印;
  4. 咱們設置在程序執行一秒後再次訂閱 subject,並稱其爲訂閱者B。這意味着在它開始訂閱以前,subject 就已經廣播過五次了。因爲咱們在建立 subject 時就設置了數據的過時時間爲 100ms,而廣播間隔爲 200ms,因此訂閱者B 在開始訂閱後只會收到前五次廣播中最後一次的值。

AsyncSubject

BehaviorSubject 和 ReplaySubject 均可以用來存儲一些數據,而 AsyncSubject 就不同了。AsyncSubject 只會在 Observable execution 完成後,將其最終值發給訂閱者。請看代碼:

import * as Rx from "rxjs";

const subject = new Rx.AsyncSubject();

// 訂閱者A
subject.subscribe((data) => {
  console.log('Subscriber A:', data);
});

subject.next(Math.random())
subject.next(Math.random())
subject.next(Math.random())

// 訂閱者B
subject.subscribe((data) => {
  console.log('Subscriber B:', data);
});

subject.next(Math.random());
subject.complete();

// Subscriber A: 0.4447275989704571
// Subscriber B: 0.4447275989704571
複製代碼

雖然這段代碼的輸出並很少,但咱們仍是照例解讀一下:

  1. 建立 AsyncSubject;
  2. 訂閱 subject 並稱其爲訂閱者A;
  3. subject 連續廣播三次,但什麼也沒發生;
  4. 再次訂閱 subject 並稱其爲訂閱者B;
  5. subject 又廣播了一次,但仍是什麼也沒發生;
  6. subject 完成。兩個訂閱者這才收到傳來的值並打印至終端。

結論

BehaviorSubject、ReplaySubject 和 AsyncSubject 均可以像 Subject 同樣被用於多播。它們各自都有一些額外的特性以適用於不一樣場景。

譯者注:這個結論好敷衍。。。

相關文章
相關標籤/搜索