天天看點

Observable與Subject

#介紹

首先,對象關系上,Subject是Observable的子類,相比擁有了多點傳播的效果,使得多個訂閱者訂閱的對象資料是共享的,而在它下面又細分了AnonymousSubject, AsyncSubject, BehaviorSubject三個子類,分别用于處理不同場景的業務需求。

#Observable和Subject的差異

  • Observable的每個訂閱者之間,讀取的釋出資料是相對各自獨立的。
  • Subject的訂閱者之間,是共享一個釋出資料的。

#Subject的子類詳情

1. BehaviorSubject

Subject的主要子類是BehaviorSubject,使用BehaviorSubject具有存儲目前“值”的特性,這意味着我們始終可以直接從BehaviorSubject擷取最後發出的值。

擷取值一共有兩種方法

  1. 通過通路.value屬性來擷取值
  2. 通過訂閱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
           
  1. 我們首先建立一個主題并使用Subscriber A訂閱它。然後,Subject将發出它的值,訂閱者A将記錄随機數。
  2. 主題發出它的下一個值。訂戶A将再次記錄此資訊
  3. 訂閱者B從訂閱主題開始。由于主題是BehaviorSubject,新訂閱者将自動接收最後存儲的值并記錄此資訊。
  4. 主題再次發出新值。現在,兩個訂閱者都将收到值并記錄它們。
  5. 最後,我們隻需通路該.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

繼續閱讀