30 天精通 RxJS(23): Subject, BehaviorSubject, ReplaySubject, AsyncSubject

轉(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"

JSBin

這裡我們可以直接用 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>
    }
}

JSBin | JSFiddle

從上面的程式碼可以看出來笼吟,因?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è)變形各自的特性介紹键俱,不知道讀者麼是否有收穫呢兰绣? 如果有任何問題,歡迎在下方留言給我方妖!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末狭魂,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子党觅,更是在濱河造成了極大的恐慌雌澄,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件杯瞻,死亡現(xiàn)場(chǎng)離奇詭異镐牺,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)魁莉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門睬涧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人旗唁,你說我怎么就攤上這事畦浓。” “怎么了检疫?”我有些...
    開封第一講書人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵讶请,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我屎媳,道長(zhǎng)夺溢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任烛谊,我火速辦了婚禮风响,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘丹禀。我一直安慰自己状勤,他們只是感情好鞋怀,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著荧降,像睡著了一般接箫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上朵诫,一...
    開封第一講書人閱讀 49,792評(píng)論 1 290
  • 那天辛友,我揣著相機(jī)與錄音,去河邊找鬼剪返。 笑死废累,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的脱盲。 我是一名探鬼主播邑滨,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼钱反!你這毒婦竟也來了掖看?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤面哥,失蹤者是張志新(化名)和其女友劉穎哎壳,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體尚卫,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡归榕,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了吱涉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片刹泄。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖怎爵,靈堂內(nèi)的尸體忽然破棺而出特石,到底是詐尸還是另有隱情,我是刑警寧澤鳖链,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布县匠,位于F島的核電站,受9級(jí)特大地震影響撒轮,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜贼穆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一题山、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧故痊,春花似錦顶瞳、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽焰络。三九已至,卻和暖如春符喝,著一層夾襖步出監(jiān)牢的瞬間闪彼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工协饲, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留畏腕,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓茉稠,卻偏偏與公主長(zhǎng)得像描馅,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子而线,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 創(chuàng)建一個(gè)Observable Observable.create()該方法接收一個(gè)Obsubscribe對(duì)象 來個(gè)...
    剩下的只有自己閱讀 1,020評(píng)論 0 0
  • 本文章內(nèi)部分圖片資源來自RayWenderlich.com 本文結(jié)合自己的理解來總結(jié)介紹一下RxSwift最基本的...
    FKSky閱讀 2,863評(píng)論 4 14
  • 介紹 RxJS是一個(gè)異步編程的庫(kù)铭污,同時(shí)它通過observable序列來實(shí)現(xiàn)基于事件的編程。它提供了一個(gè)核心的類型:...
    泓滎閱讀 16,590評(píng)論 0 12
  • 在前邊RxJava實(shí)戰(zhàn)技巧大全一文中膀篮,我們介紹了RxJava使用過程中常見的應(yīng)用場(chǎng)景和可能遇到的問題嘹狞,其中我們談到...
    涅槃1992閱讀 4,808評(píng)論 4 34
  • 這是轉(zhuǎn)載【30天精通 RxJS】的 05 篇,如果還沒看過 04 篇可以往這邊走:30 天精通 RxJS (04)...
    readilen閱讀 3,109評(píng)論 3 14