【Rxjs】 - 解析四種主題Subject

引言

開發ngx(angular 2+)應用時,基本上處處都會用到rxjs來處理異步請求,事件調用等等。因此常常會使用Subject來處理源源不斷的數據流,好比input text change, toast notification等等。
這都要依賴於Subject自己既能夠是Observable也能夠是Observer,也就是說subject既能夠做爲一個數據源,也能夠自己成爲一組訂閱者的代理
但當處理更加複雜的業務需求時,僅僅用Subject可能沒法知足要求,這個時候就考慮一下rxjs提供的其餘Subject Class了, 例如BehaviorSubject ReplaySubject AsyncSubject, 接下來咱們就來看一下他們跟Subject有什麼區別,各自有什麼特色,在何時更適合使用。typescript

[文中代碼均使用typescript]異步

Subject

首先咱們來建立一個Rxjs Subject, 數據的類型是numberasync

let subject1: Subject<number> = new Subject<number>();  // (A)

而後咱們使用Subjectnext方法來emit(發射)1條數據spa

subject1.next(100); (B)

接下來對subject1建立兩個訂閱,在subscription中直接打印接受到的數據代理

subject1.subscribe((res: number) => console.info("subjectA ", res));  // (C)
 subject1.subscribe((res: number) => console.info("subjectB ", res));

接下來我在發射兩條數據code

subject1.next(200); (D)
 subject1.next(300);

好了,接下來咱們就來看看console裏面會打印出什麼結果。
也許有的同窗會以爲結果是這樣的,由於Subject能夠接收源源不斷的數據嘛,因此不管發射多少次數據,訂閱者都能接收到。server

//output
subjectA 100
subjectB 100
subjectA 200
subjectB 200
subjectA 300
subjectB 300

這個結果不太對
由於Subject的訂閱者只有在訂閱後,才能接收到數據源發射過來的值。
因此在代碼塊C中, 訂閱者在訂閱數據源subject1以前, 不管代碼塊B執行多少次, 訂閱者也只能收到代碼C以後發射的數據。blog

正確的結果應該是:rxjs

//output
subjectA 200
subjectB 200
subjectA 300
subjectB 300

這種狀況在項目裏常常能遇到,有時候我明明從數據源發射一個數據,但在訂閱者拿到的值倒是undefined或者null, 這就是由於訂閱者是在數據源發射以後建立的,天然沒法接收到數據了。
假如咱們想在訂閱者建立以後,不管何時都能拿到數據, 這應該怎麼辦呢? 那就要考慮使用BehaviourSubject了。事件

BehaviorSubject

咱們依舊使用剛纔的例子, 建立一個BehaviorSubject, 默認值設爲0. BehaviorSubject須要給個默認值
而後發射一條數據100,建立一個訂閱者,再發射一條數據200,再建立一個訂閱者,最後發射一條數據300。
代碼以下:

let subject2: BehaviorSubject<number> = new BehaviorSubject<number>(0);
subject2.next(100);
subject2.subscribe((res: number) => console.info("behavior-subjectA ", res));
subject2.next(200);
subject2.subscribe((res: number) => console.info("behavior-subjectB ", res));
subject2.next(300);

這個時候結果就應該是:

//output
behavior-subjectA 100
behavior-subjectA 200
behavior-subjectB 200
behavior-subjectA 300
behavior-subjectB 300

因爲BehaviorSubject是能夠存儲最後一條數據或者初始默認值的, 因此不管訂閱者何時訂閱到數據源subject2上, 都能接收到數據。
因此針對訂閱者behavior-subjectA, 他訂閱的時候,數據流裏最後一條數據是100, 他能當即接收到。 而後依次能接收到最新的數據200300
針對訂閱者behavior-subjectB, 他訂閱的時候,數據流裏最後一條數據是200, 他能當即接收到。 而後只能能接收到最新的數據300了。

BehaviorSubject給予咱們的便利就是,不管什麼時候訂閱到數據源,始終都能拿到最新的或者初始的數據,但也只能拿到一條數據,但當咱們處理input text change事件時,須要拿到用戶輸入的全部字符,也就是數據流的全部數據,BehaviorSubject就無能爲力了,這個時候咱們考慮使用ReplaySubject了。

ReplaySubject

咱們依舊使用剛纔的例子, 建立一個ReplaySubject, 發射兩條數據100和200,建立一個訂閱者,再發射一條數據300,再建立一個訂閱者,最後發射一條數據400。
代碼以下:

let subject3: ReplaySubject<number> = new ReplaySubject<number>();
subject3.next(100);
subject3.next(200);
subject3.subscribe((res: number) => console.info("replay-subjectA ", res));
subject3.next(300);
subject3.subscribe((res: number) => console.info("replay-subjectB ", res));
subject3.next(400);

控制打印的結果將是:

//output
replay-subjectA 100
replay-subjectA 200
replay-subjectA 300
replay-subjectB 100
replay-subjectB 200
replay-subjectB 300
replay-subjectA 400
replay-subjectB 400

ReplaySubject會存儲數據流中的全部數據不管什麼時候訂閱到subject3,訂閱者都能獲取了訂閱以前數據流裏的全部數據,而後依舊獲取到接下來獲取的到的新數據。
就像ReplaySubject類名的中Replay, 一旦訂閱到數據源,就會將數據流像放電影同樣從新放一遍給你。

訂閱者replay-subjectA訂閱到subject3的時候,數據流裏已經有了100和200, 接收並打印出來。
最後打印新數據300和400.
訂閱者replay-subjectB訂閱到subject3的時候,數據流裏已經有了100,200,300, 接收並打印出來。最後打印新數據400.

接下來就要說下最後一種Subject了,也就是AsyncSubject

AsyncSubject

AsyncSubjectBehaviorSubject`ReplaySubject`有些相似,但不一樣的是AsyncSubject只會存儲數據流裏的最後一條數據, 並且只有在數據流complete時纔會將值發佈出去
AsyncSubject主要是用來處理異步操做,當數據源是異步請求或者事件處理時,可能會發射出不少數據,若是咱們只但願數據源的異步操做完成的時候,訂閱者才能接收到值,這個時候就可使用AsyncSubject了。
接下來咱們看個例子,
建立一個AsyncSubject, 而後發射數據,建立訂閱者,再發射數據。。。

let subject4: AsyncSubject<number> = new AsyncSubject<number>();
subject4.next(100);
subject4.next(100);
subject4.subscribe((res: number) => console.info("async-subjectA ", res));
subject4.next(300);
subject4.subscribe((res: number) => console.info("async-subjectB ", res));
subject4.next(400);
subject4.subscribe((res: number) => console.info("async-subjectC ", res));
subject4.complete();
subject4.subscribe((res: number) => console.info("async-subjectD ", res));
subject4.next(500);

最後的結果就應該是:

//output4
async-subjectA 400
async-subjectB 400
async-subjectC 400
async-subjectD 400

因爲subject4AsyncSubject, 只有在complete的時候纔會向訂閱者publish數據,並且只publish最後一次數據,因此不管訂閱者什麼時候訂閱數據源,均可以接收到最後一次數據。
但爲何沒有打印出500呢,由於數據源已經complete了,你就沒法再發射新數據了。

總結

最後來總結一下四種Subject的特色,理解好的各自的特色,在項目開發中能夠處理不少棘手的需求,同時也會避免不少問題的發生。
圖片描述

相關文章
相關標籤/搜索