開發ngx(angular 2+)應用時,基本上處處都會用到rxjs
來處理異步請求,事件調用等等。因此常常會使用Subject
來處理源源不斷的數據流,好比input text change, toast notification等等。
這都要依賴於Subject
自己既能夠是Observable
也能夠是Observer
,也就是說subject
既能夠做爲一個數據源
,也能夠自己成爲一組訂閱者的代理
。
但當處理更加複雜的業務需求時,僅僅用Subject
可能沒法知足要求,這個時候就考慮一下rxjs
提供的其餘Subject Class
了, 例如BehaviorSubject
ReplaySubject
AsyncSubject
, 接下來咱們就來看一下他們跟Subject
有什麼區別,各自有什麼特色,在何時更適合使用。typescript
[文中代碼均使用typescript
]異步
首先咱們來建立一個Rxjs Subject
, 數據的類型是number
async
let subject1: Subject<number> = new Subject<number>(); // (A)
而後咱們使用Subject
的next
方法來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
, 默認值設爲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
, 他能當即接收到。 而後依次能接收到最新的數據200
和300
。
針對訂閱者behavior-subjectB
, 他訂閱的時候,數據流裏最後一條數據是200
, 他能當即接收到。 而後只能能接收到最新的數據300
了。
BehaviorSubject
給予咱們的便利就是,不管什麼時候訂閱到數據源,始終都能拿到最新的或者初始的數據,但也只能拿到一條數據
,但當咱們處理input text change
事件時,須要拿到用戶輸入的全部字符,也就是數據流的全部數據,BehaviorSubject
就無能爲力了,這個時候咱們考慮使用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
和BehaviorSubject
`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
因爲subject4
是AsyncSubject
, 只有在complete
的時候纔會向訂閱者publish
數據,並且只publish
最後一次數據,因此不管訂閱者什麼時候訂閱數據源,均可以接收到最後一次數據。
但爲何沒有打印出500
呢,由於數據源已經complete
了,你就沒法再發射新數據了。
最後來總結一下四種Subject
的特色,理解好的各自的特色,在項目開發中能夠處理不少棘手的需求,同時也會避免不少問題的發生。