#介紹
首先,對象關系上,Subject是Observable的子類,相比擁有了多點傳播的效果,使得多個訂閱者訂閱的對象資料是共享的,而在它下面又細分了AnonymousSubject, AsyncSubject, BehaviorSubject三個子類,分别用于處理不同場景的業務需求。
#Observable和Subject的差異
- Observable的每個訂閱者之間,讀取的釋出資料是相對各自獨立的。
- Subject的訂閱者之間,是共享一個釋出資料的。
#Subject的子類詳情
1. BehaviorSubject
Subject的主要子類是BehaviorSubject,使用BehaviorSubject具有存儲目前“值”的特性,這意味着我們始終可以直接從BehaviorSubject擷取最後發出的值。
擷取值一共有兩種方法
- 通過通路.value屬性來擷取值
- 通過訂閱BehaviorSubject來擷取值,BehaviorSubject将直接向訂閱者發出目前值。即使訂閱者訂閱的時間遠遠晚于存儲的值。
import * as Rx from "rxjs";
const subject = new Rx.BehaviorSubject();
// 訂閱對象1
subject.subscribe((data) => {
console.log('Subscriber A:', data);
});
//釋出新的值
subject.next(Math.random());
subject.next(Math.random());
// 訂閱對象2
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
- 我們首先建立一個主題并使用Subscriber A訂閱它。然後,Subject将發出它的值,訂閱者A将記錄随機數。
- 主題發出它的下一個值。訂戶A将再次記錄此資訊
- 訂閱者B從訂閱主題開始。由于主題是BehaviorSubject,新訂閱者将自動接收最後存儲的值并記錄此資訊。
- 主題再次發出新值。現在,兩個訂閱者都将收到值并記錄它們。
- 最後,我們隻需通路該.value屬性即可記錄目前的Subjects值 ,因為它是同步的,可以不用通過訂閱讀取值。
最後,我發現現在已經需要在建立對象的時候賦予初始值,如下示例:
import * as Rx from "rxjs";
// 參數:初始值
const subject = new Rx.BehaviorSubject(Math.random());
2.ReplaySubject
ReplaySubject可以與BehaviorSubject相比,它可以向新訂閱者發送“舊”的曆史值。借此特性,我們可以用它可以記錄可觀察對象執行的一部分,是以存儲多個舊值并将它們“重放”給新的訂閱者。
建立ReplaySubject時,你可以指定要存儲的值的數量以及要存儲它們的時間長度。
import * as Rx from "rxjs";
// 存儲2個值,這些值在新訂閱之前的最後一秒執行
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
根據輸出來看,訂閱者A記錄了三個釋出值。
由于我們告訴ReplaySubject存儲2個值,它将直接向訂閱者B發送最後兩個值,訂閱者B于是隻輸出了最後釋出的兩個值。
除此之外,我們還可以指定存儲值的時間,如下所示。
import * as Rx from "rxjs";
//建立ReplaySubject并指定隻存儲最後2個值,但不超過100毫秒
const subject = new Rx.ReplaySubject(2, 100);
// 建立訂閱A
subject.subscribe((data) => {
console.log('Subscriber A:', data);
});
// 每隔200毫秒開始發出主題值。訂閱者A将選擇此項并記錄主題發出的每個值
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
如上所示,我們開始訂閱者B後,因為我們在1000毫秒後執行此操作,這意味着在開始訂閱之前,主題已經發出了5個值。
這意味着在1000毫秒之後,當訂閱者B開始訂閱時,它将僅接收1個值,因為主體每200ms發出一次值。
3. AsyncSubject
雖然BehaviorSubject和ReplaySubject都存儲值,但AsyncSubject的工作方式略有不同。AsyncSubject是一個主題變體,隻有Observable執行的最後一個值才會發送給它的訂閱者,并且隻有在執行完成時才會發送.
import * as Rx from "rxjs";
// 建立了AsyncSubject
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
如上我們能夠觀察到,A并沒有輸出釋出的前三個值,是因為隻有當訂閱對象complete完成後,才會将值發送給訂閱者。
#結論
BehaviorSubject,ReplaySubject和AsyncSubject不僅可以像使用普通Subject一樣用于多點傳播,而且它們在不同場景中各自有具有非常友善的特性,我們可以結合業務場景和需求具體使用。
PS: 如上教程多為譯文,原文位址如下(可能需要VPN才能通路):
原文:https://medium.com/@luukgruijs/understanding-rxjs-behaviorsubject-replaysubject-and-asyncsubject-8cc061f1cfc0