轉(zhuǎn)載
昨天我們介紹了 Subject 是什麼乾颁,今天要講 Subject 一些應(yīng)用方式,以及 Subject 的另外三種變形。
Subject
昨天我們講到了 Subject 實(shí)際上就是 Observer Pattern 的實(shí)作,他會(huì)在內(nèi)部管理一份 observer 的清單闸天,并在接收到值時(shí)遍歷這份清單并送出值,所以我們可以這樣用 Subject
var subject = new Rx.Subject();
var observerA = {
next: value => console.log('A next: ' + value),
error: error => console.log('A error: ' + error),
complete: () => console.log('A complete!')
}
var observerB = {
next: value => console.log('B next: ' + value),
error: error => console.log('B error: ' + error),
complete: () => console.log('B complete!')
}
subject.subscribe(observerA);
subject.subscribe(observerB);
subject.next(1);
// "A next: 1"
// "B next: 1"
subject.next(2);
// "A next: 2"
// "B next: 2"
這裡我們可以直接用 subject 的 next 方法傳送值斜做,所有訂閱的 observer 就會(huì)接收到苞氮,又因?yàn)?Subject 本身是 Observable,所以這樣的使用方式很適合用在某些無法直接使用 Observable 的前端框架中瓤逼,例如在 React 想對(duì) DOM 的事件做監(jiān)聽
class MyButton extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
this.subject = new Rx.Subject();
this.subject
.mapTo(1)
.scan((origin, next) => origin + next)
.subscribe(x => {
this.setState({ count: x })
})
}
render() {
return <button onClick={event => this.subject.next(event)}>{this.state.count}</button>
}
}
從上面的程式碼可以看出來笼吟,因?yàn)?React 本身 API 的關(guān)系,如果我們想要用 React 自訂的事件抛姑,我們沒辦法直接使用 Observable 的 creation operator 建立 observable赞厕,這時(shí)就可以靠 Subject 來做到這件事。
Subject 因?yàn)橥瑫r(shí)是 observer 和 observable定硝,所以應(yīng)用面很廣除了前面所提的之外皿桑,還有上一篇文章講的組播(multicase)特性也會(huì)在接下來的文章做更多應(yīng)用的介紹,這裡先讓我們來看看 Subject 的三個(gè)變形。
BehaviorSubject
很多時(shí)候我們會(huì)希望 Subject 能代表當(dāng)下的狀態(tài)诲侮,而不是單存的事件發(fā)送镀虐,也就是說如果今天有一個(gè)新的訂閱,我們希望 Subject 能立即給出最新的值沟绪,而不是沒有回應(yīng)刮便,例如下面這個(gè)例子
var subject = new Rx.Subject();
var observerA = {
next: value => console.log('A next: ' + value),
error: error => console.log('A error: ' + error),
complete: () => console.log('A complete!')
}
var observerB = {
next: value => console.log('B next: ' + value),
error: error => console.log('B error: ' + error),
complete: () => console.log('B complete!')
}
subject.subscribe(observerA);
subject.next(1);
// "A next: 1"
subject.next(2);
// "A next: 2"
subject.next(3);
// "A next: 3"
setTimeout(() => {
subject.subscribe(observerB); // 3 秒后才訂閱,observerB 不會(huì)收到任何值绽慈。
},3000)
以上面這個(gè)例子來說恨旱,observerB 訂閱的之后,是不會(huì)有任何元素送給 observerB 的坝疼,因?yàn)樵谶@之后沒有執(zhí)行任何 subject.next()
搜贤,但很多時(shí)候我們會(huì)希望 subject 能夠表達(dá)當(dāng)前的狀態(tài),在一訂閱時(shí)就能收到最新的狀態(tài)是什麼钝凶,而不是訂閱后要等到有變動(dòng)才能接收到新的狀態(tài)仪芒,以這個(gè)例子來說,我們希望 observerB 訂閱時(shí)就能立即收到 3
耕陷,希望做到這樣的效果就可以用 BehaviorSubject掂名。
BehaviorSubject 跟 Subject 最大的不同就是 BehaviorSubject 是用來呈現(xiàn)當(dāng)前的值,而不是單純的發(fā)送事件哟沫。BehaviorSubject 會(huì)記住最新一次發(fā)送的元素饺蔑,并把該元素當(dāng)作目前的值,在使用上 BehaviorSubject 建構(gòu)式需要傳入一個(gè)參數(shù)來代表起始的狀態(tài)嗜诀,范例如下
var subject = new Rx.BehaviorSubject(0); // 0 為起始值
var observerA = {
next: value => console.log('A next: ' + value),
error: error => console.log('A error: ' + error),
complete: () => console.log('A complete!')
}
var observerB = {
next: value => console.log('B next: ' + value),
error: error => console.log('B error: ' + error),
complete: () => console.log('B complete!')
}
subject.subscribe(observerA);
// "A next: 0"
subject.next(1);
// "A next: 1"
subject.next(2);
// "A next: 2"
subject.next(3);
// "A next: 3"
setTimeout(() => {
subject.subscribe(observerB);
// "B next: 3"
},3000)
JSBin | JSFiddle
從上面這個(gè)范例可以看得出來 BehaviorSubject 在建立時(shí)就需要給定一個(gè)狀態(tài)膀钠,并在之后任何一次訂閱,就會(huì)先送出最新的狀態(tài)裹虫。其實(shí)這種行為就是一種狀態(tài)的表達(dá)而非單存的事件,就像是年齡跟生日一樣融击,年齡是一種狀態(tài)而生日就是事件筑公;所以當(dāng)我們想要用一個(gè) stream 來表達(dá)年齡時(shí),就應(yīng)該用 BehaviorSubject尊浪。
ReplaySubject
在某些時(shí)候我們會(huì)希望 Subject 代表事件匣屡,但又能在新訂閱時(shí)重新發(fā)送最后的幾個(gè)元素,這時(shí)我們就可以用 ReplaySubject拇涤,范例如下
var subject = new Rx.ReplaySubject(2); // 重複發(fā)送最后 2 個(gè)元素
var observerA = {
next: value => console.log('A next: ' + value),
error: error => console.log('A error: ' + error),
complete: () => console.log('A complete!')
}
var observerB = {
next: value => console.log('B next: ' + value),
error: error => console.log('B error: ' + error),
complete: () => console.log('B complete!')
}
subject.subscribe(observerA);
subject.next(1);
// "A next: 1"
subject.next(2);
// "A next: 2"
subject.next(3);
// "A next: 3"
setTimeout(() => {
subject.subscribe(observerB);
// "B next: 2"
// "B next: 3"
},3000)
JSBin |
可能會(huì)有人以為 ReplaySubject(1)
是不是就等同于 BehaviorSubject捣作,其實(shí)是不一樣的,BehaviorSubject 在建立時(shí)就會(huì)有起始值鹅士,比如 BehaviorSubject(0)
起始值就是 0
券躁,BehaviorSubject 是代表著狀態(tài)而 ReplaySubject 只是事件的重放而已。
AsyncSubject
AsyncSubject 是最怪的一個(gè)變形,他有點(diǎn)像是 operator last
也拜,會(huì)在 subject 結(jié)束后送出最后一個(gè)值以舒,范例如下
var subject = new Rx.AsyncSubject();
var observerA = {
next: value => console.log('A next: ' + value),
error: error => console.log('A error: ' + error),
complete: () => console.log('A complete!')
}
var observerB = {
next: value => console.log('B next: ' + value),
error: error => console.log('B error: ' + error),
complete: () => console.log('B complete!')
}
subject.subscribe(observerA);
subject.next(1);
subject.next(2);
subject.next(3);
subject.complete();
// "A next: 3"
// "A complete!"
setTimeout(() => {
subject.subscribe(observerB);
// "B next: 3"
// "B complete!"
},3000)
JSBin |
從上面的程式碼可以看出來,AsyncSubject 會(huì)在 subject 結(jié)束后才送出最后一個(gè)值慢哈,其實(shí)這個(gè)行為跟 Promise 很像蔓钟,都是等到事情結(jié)束后送出一個(gè)值,但實(shí)務(wù)上我們非常非常少用到 AsyncSubject卵贱,絕大部分的時(shí)候都是使用 BehaviorSubject 跟 ReplaySubject 或 Subject滥沫。
我們把 AsyncSubject 放在大腦的深處就好
今日小結(jié)
今天介紹了 Subject 的一些應(yīng)用方式,以及 BehaviorSubject, ReplaySubject, AsyncSubject 三個(gè)變形各自的特性介紹键俱,不知道讀者麼是否有收穫呢兰绣? 如果有任何問題,歡迎在下方留言給我方妖!