【武漢-第149期】如何理解RxJS?

一.背景介紹

Rx(Reactive Extension -- 響應(yīng)式擴展 http://reactivex.io )最近在各個領(lǐng)域都非扯途校火。其實Rx這個貨是微軟在好多年前針對C#寫的一個開源類庫介评,但好多年都不溫不火椭更,一直到Netflix針對Java平臺做出了RxJava版本后才在開源社區(qū)熱度飛速躥升蚪燕。

二.知識剖析

我們從最基礎(chǔ)的異步回調(diào)講起,然后再從? Promise過渡到 RXJS锦募。

異步回調(diào):

在我們平時編程中摆屯,當(dāng)需要解決異步操作時,用得最多的應(yīng)該就是把回調(diào)函數(shù)當(dāng)做參數(shù)傳遞給異步函數(shù)了吧

function print_msg(msg) {

//Do something with msg

console.log(msg);

}

function async_read(callback) {

// Some async_work start

// Async get data from a PORT into msg

// Some async_work end

callback(msg);

}

async_read(print_msg);

在例1中御滩,我們通過傳遞 print_msg 這個回調(diào)函數(shù)給異步操作 async_read 以達到當(dāng)異步操作完成時輸出從端口讀到的 msg 這個目的鸥拧。

這樣看起來,這種方式簡單易懂削解,但是富弦,真的很好用嗎?想象一下氛驮,我們所傳遞的回調(diào)函數(shù)也是一個異步操作腕柜,也需要傳入一個回調(diào)函數(shù)來處理異步結(jié)果,如例子2:

function my_console(value2) {

//Do something with value2

console.log(value2);

}

function async_work1(callback1, callback2) {

// some async_work start

//......get value1

// some async_work end

callback1(value1, callback2);

}

function async_work2(value1, callback2) {

// some async_work start and do something with value1

//......get value2

// some async_work end

callback2(value2);

}

async_work1(async_work2, my_console);

在例2中,我們將回調(diào)函數(shù)1和回調(diào)函數(shù)2傳給了異步操作1盏缤,以便在異步操作1完成時調(diào)用回調(diào)函數(shù)1砰蠢,再將異步操作1得到的 value1 與回調(diào)函數(shù)2傳給回調(diào)函數(shù)1,最終當(dāng)回調(diào)函數(shù)1完成后將調(diào)用回調(diào)函數(shù)2輸出異步操作2得到的 value2 唉铜。當(dāng)回調(diào)函數(shù)2也是異步操作的時候怎么辦台舱?難道真的這樣一層套一層?這樣的代碼讀起來晦澀易懂潭流,我們要通過某種方式將其變得更簡單竞惋,于是 Promise 出現(xiàn)了!

Promise:

Promise 很好的將這種嵌套式調(diào)用轉(zhuǎn)變成了鏈?zhǔn)秸{(diào)用灰嫉,使得代碼的可讀性維護性都更高拆宛。對于例1,我們可以這樣:

var promise = new Promise(function(resolve, reject) {

// Some async_work start

// Async get data from a PORT into msg

// Some async_work end

if (/* Async_work successed */) {

resolve(msg);

}

else {

reject(error);

}

});

promise.then(function(msg) {

//Do something with msg

console.log(msg)

}, function(error) {

// Failure, do something here

console.log(error);

});

在上例中讼撒,我們創(chuàng)建了一個 Promise 對象浑厚,并且傳入了一個函數(shù)對象,注意這個函數(shù)并不是異步操作結(jié)束后將被調(diào)用的函數(shù)根盒,而是用來初始化 Promise 的钳幅。這個函數(shù)接受2個參數(shù) resolve 和 reject (后者可選),并且我們將異步操作全部移植入到這個函數(shù)中郑象,當(dāng)異步操作執(zhí)行成功之后贡这,調(diào)用了 resolve(msg)茬末,這是什么意思厂榛?很簡單,如果我們用第一種方法丽惭,resolve 這一行肯定是 callback(msg)击奶,也就是調(diào)用回調(diào)函數(shù)并將異步得到的 msg 傳給其。所以這里? resolve(msg) 的意思就是通知注冊號的回調(diào)函數(shù)異步操作已經(jīng)完成了责掏,并且產(chǎn)出了可用的 msg 參數(shù)柜砾。那么回調(diào)函數(shù)是在哪里注冊的呢?可用看到换衬,之后我們又調(diào)用了 promise 的 then 方法痰驱,它接收一個函數(shù)對象,不錯這個函數(shù)對象就是我們的回調(diào)函數(shù)瞳浦。但是我們的 then 居然接收了2個回調(diào)函數(shù)担映, 很顯然,第二個函數(shù)是用來處理 reject 通知過來的 error 的叫潦。

既然有了 Promise蝇完,那么何必再加入 RXJS 這個玩意呢?

Promise 有一個缺點,那便是一旦調(diào)用了 resolve 或者 reject 之后便返回了短蜕,不能再次 resolve 或者 reject氢架,想象一下,若是從端口源源不斷地發(fā)來消息朋魔,每次收到消息就要通知回調(diào)函數(shù)來處理岖研,那該怎么辦呢?

于是警检,偉大的 RXJS 又出現(xiàn)了6忻怠!

RXJS:

我們已經(jīng)知道了 Promise 的作用和用法解滓,通過 Promise 對象赃磨,我們可以在完成異步工作之后調(diào)用 resolve(X) 通知回調(diào)函數(shù)異步操作已經(jīng)完成了,并且生產(chǎn)了可使用的 X 對象洼裤。既然 RXJS 比 Promise 更厲害邻辉,那么它當(dāng)然也可以完成這個任務(wù),并且可以做得更好腮鞍。

先從官網(wǎng)搬來rxjs的幾個實例概念

Observable: 可觀察的數(shù)據(jù)序列.

Observer: 觀察者實例值骇,用來決定何時觀察指定數(shù)據(jù).

Subscription: 觀察數(shù)據(jù)序列返回訂閱實例.

Operators: Observable的操作方法,包括轉(zhuǎn)換數(shù)據(jù)序列移国,過濾等吱瘩,所有的Operators方法接受的參數(shù)是上一次發(fā)送的數(shù)據(jù)變更的值,而方法返回值我們稱之為發(fā)射新數(shù)據(jù)變更.

Subject: 被觀察對象.

Schedulers: 控制調(diào)度并發(fā)迹缀,即當(dāng)Observable接受Subject的變更響應(yīng)時使碾,可以通過scheduler設(shè)置響應(yīng)方式,目前內(nèi)置的響應(yīng)可以調(diào)用Object.keys(Rx.Subject)查看祝懂。

JSBin這個在線Javascript IDE

記得要添加庫


三.常見問題

Observable到底是什么

先上代碼:

let foo = Rx.Observable.create(observer => {

console.log('Hello');

observer.next(42);

});

foo.subscribe(x => console.log(x));

foo.subscribe(y => console.log(y));


這里可以把foo想象成一個函數(shù)票摇,這意味著你每次調(diào)用foo都會導(dǎo)致傳入Rx.Observable.create里的回調(diào)函數(shù)重新執(zhí)行一次, 調(diào)用的方式為foo.subscribe(callback), 相當(dāng)于foo()。 接收函數(shù)返回值的方式也從var a = foo()改為通過傳入回調(diào)函數(shù)的方式獲取砚蓬。第三行的observer.next表示返回一個值, 你可以調(diào)用多次矢门,每次調(diào)用observer.next后, 會先將next里的值返回給foo.subcribe里的回調(diào)函數(shù), 執(zhí)行完后再返回灰蛙。observer.complete, observer.error來控制流程祟剔。 具體看代碼:

var observable = Rx.Observable.create(observer => {

try {

observer.next(1);

console.log('hello');

observer.next(2);

observer.next(3);

observer.complete();

observer.next(4);

} catch (err) {

observer.error(err);

}

});

let subcription = observable.subscribe(value => {

console.log(value)

})


如上的第一個回調(diào)函數(shù)里的結(jié)構(gòu)是推薦的結(jié)構(gòu)。 當(dāng)observable的執(zhí)行出現(xiàn)異常的時候摩梧,通過observer.error將錯誤返回, 然而observable.subscribe的回調(diào)函數(shù)無法接收到.因為observer.complete已經(jīng)調(diào)用, 因此observer.next(4)的返回是無效的. Observable不是可以返回多個值的Promise物延。 雖然獲得Promise的值的方式也是通過then函數(shù)這種類似的方式, 但是new Promise(callback)里的callback回調(diào)永遠(yuǎn)只會執(zhí)行一次!因為Promise的狀態(tài)是不可逆的障本。

Observer是什么

先看代碼:

let foo = Rx.Observable.create(observer => {

console.log('Hello');

observer.next(42);

});

let observer = x => console.log(x);

foo.subscribe(observer);

代碼中的第二個變量就是observer. 沒錯教届, observer就是當(dāng)Observable"返回"值的時候接受那個值的函數(shù)!第五行中的observer其實就是通過foo.subscribe傳入的callback. 只不過稍加封裝了响鹃。 怎么封裝的? 看代碼:

var observable = Rx.Observable.create(observer => {

try {

observer.next(1);

console.log('hello');

observer.next(2);

observer.next(3);

observer.complete();

observer.next(4);

} catch(err) {

observer.error(err);

}

});

var observer = {

next(value) { console.log(value) ;},

complete() { console.log('completed');},

error(err) { console.error(err); }

}

let subcription = observable.subscribe(observer);

你看到observer被定義成了一個對象案训, 其實這才是完整的observer. 傳入一個callback到observable.subcribe相當(dāng)于傳入了 { next: callback }买置。


Subcription里的陷阱

Subscription是什么, 先上代碼:

var observable = Rx.Observable.interval(1000);

var subscription = observable.subscribe(x => console.log(x));

setTimeout(() => {

subscription.unsubscribe();

}, 3100)

Rx.Observable.interval可以返回一個能夠發(fā)射(返回)0强霎, 1忿项, 2, 3...城舞, n數(shù)字的Observable轩触, 返回的時間間隔這里是1000ms。 第二行中的變量就是subscription家夺。 subscription有一個unsubscribe方法, 這個方法可以讓subscription訂閱的observable發(fā)射的數(shù)據(jù)被observer忽略掉脱柱。 通俗點說就是取消訂閱。


unsubscribe存在一個陷阱拉馋。 先看代碼:

var foo = Rx.Observable.create((observer) => {

var i = 0

setInterval(() => {

observer.next(i++)

console.log('hello')

}, 1000)

})

const subcription = foo.subscribe((i) => console.log(i))

subcription.unsubscribe()

剛才說了榨为, unsubscribe只會讓observer忽略掉observable發(fā)射的數(shù)據(jù),但是setInterval依然會繼續(xù)執(zhí)行煌茴。 這看起來似乎是一個愚蠢的設(shè)計随闺。 所以不建議這樣寫。


Subject

Subject是一種能夠發(fā)射數(shù)據(jù)給多個observer的Observable, 這讓Subject看起來就好像是EventEmitter蔓腐。 先上代碼:

var subject = new Rx.Subject();

subject.subscribe({

next: (v) => console.log('observerA: ' + v)

});

subject.subscribe({

next: (v) => console.log('observerB: ' + v)

});

subject.next(1);

subject.next(2);


與Observable不同的是矩乐, Subject發(fā)射數(shù)據(jù)給多個observer。 其次回论, 定義subject的時候并沒有傳入callback散罕, 這是因為subject自帶next, complete, error等方法。從而可以發(fā)射數(shù)據(jù)給observer透葛。 這和EventEmitter很類似笨使。observer并不知道他subscribe的是Obervable還是Subject。 對observer來說是透明的僚害。 而且Subject還有各種派生, 比如說:

BehaviorSubject 能夠保留最近的數(shù)據(jù)繁调,使得當(dāng)有subscribe的時候萨蚕,立馬發(fā)射出去√阋龋看代碼:

ReplaySubject 能夠保留最近的一些數(shù)據(jù)岳遥, 使得當(dāng)有subscribe的時候,將這些數(shù)據(jù)發(fā)射出去裕寨。

AsyncSubject 只會發(fā)射結(jié)束前的一個數(shù)據(jù)

Multicasted Observables 是一種借助Subject來將數(shù)據(jù)發(fā)射給多個observer的Observable浩蓉。

四.解決方案

五.代碼實戰(zhàn)

我們來舉幾個例子派继。比如說在傳統(tǒng)的編程中 a=b+c,表示將表達式的結(jié)果賦給a捻艳,而之后改變b或c 的值不會影響a驾窟。但在響應(yīng)式編程中,a的值會隨著b或c的更新而更新认轨。

JSBin這個在線Javascript IDE

傳統(tǒng)編程中b,c的變化不會影響a

var a,b=1,c=2;

a=b+c;

console.log('b=' + b);

console.log('c=' + c);

console.log('a=' + a);

b=3;

c=2;

console.log('a=' + a);


那么用響應(yīng)式編程方法寫出來就是這個樣子绅络,可以看到隨著b和c的變化a也會隨之變化。

var b$ = Rx.Observable.from([1,3]);

var c$ = Rx.Observable.from([2,2]);

var a$ = Rx.Observable.zip(b$, c$, (b,c) => {

console.log('b=' + b);

console.log('c=' + c);

return b+c;

});

a$.subscribe(a=> console.log('a=' +a));


看出來一些不一樣的思維方式了嗎嘁字?響應(yīng)式編程需要描述數(shù)據(jù)流恩急,而不是單個點的數(shù)據(jù)變量,我們需要把數(shù)據(jù)的每個變化匯聚成一個數(shù)據(jù)流纪蜒。如果說傳統(tǒng)編程方式是基于離散的點衷恭,那么響應(yīng)式編程就是線。

上面的代碼雖然很短纯续,但體現(xiàn)出Rx的一些特點

Lamda表達式匾荆,對,就是那個看上去像箭頭的東西 => 杆烁。你可以把它想象成一個數(shù)據(jù)流的指向牙丽,我們從箭頭左方取得數(shù)據(jù)流,在右方做一系列處理后或者輸出成另一個數(shù)據(jù)流或者做一些其他對于數(shù)據(jù)的操作兔魂。

操作符:這個例子中的 from, zip 都是操作符烤芦。Rx中有太多的操作符,從大類上講分為:創(chuàng)建類操作符析校、變換類操作符构罗、過濾類操作符、合并類操作符智玻、錯誤處理類操作符遂唧、工具類操作符、條件型操作符吊奢、數(shù)學(xué)和聚集類操作符盖彭、連接型操作符等等。

Rx再體驗

首先在HTML中引入Rx類庫页滚,然后定義一個id為todo的文本輸入框:

在Javascript標(biāo)簽中選擇 ES6/Babel召边,因為這樣可以直接使用ES6的語法,在文本框中輸入以下javascript裹驰。在RxJS領(lǐng)域一般在Observable類型的變量后面加上$標(biāo)識這是一個“流變量”(由英文Stream得來隧熙,Observable就是一個Stream,所以用$標(biāo)識)幻林,不是必須的贞盯,但是屬于約定俗成音念。

let todo = document.getElementById('todo');

let input$ = Rx.Observable.fromEvent(todo, 'keyup');

input$.subscribe(input => console.log(input.target.value));

如果Console窗口默認(rèn)沒有打開的話,請點擊 Console 標(biāo)簽躏敢,然后選中右側(cè)的 Run with JS 旁邊的Auto-run js復(fù)選框闷愤。在Output窗口中應(yīng)該可以看到一個文本輸入框,在這個輸入框中輸入任意你要試驗的字符父丰,觀察Console

Console和Output窗口


這幾行代碼很簡單:首先我們得到HTML中id為todo的輸入框?qū)ο蟾翁罚缓蠖x一個觀察者對象將todo這個輸入框的keyup事件轉(zhuǎn)換成一個數(shù)據(jù)流,最后訂閱這個數(shù)據(jù)流并在console中輸出我們接收到的input事件的值蛾扇。我們從這個例子中可以觀察到幾個現(xiàn)象:

數(shù)據(jù)流:你每次在輸入框中輸入時都會有新的數(shù)據(jù)被推送過來攘烛。本例中,你會發(fā)現(xiàn)連續(xù)輸入“1镀首,2坟漱,3,4”更哄,在console的輸出是“1芋齿,12,123成翩,1234”觅捆,也就是說每次keyup事件我們都得到了完整的輸入框中的值。而且這個數(shù)據(jù)流是無限的麻敌,只要我們不停止訂閱栅炒,它就會一直在那里待命。

我們觀察的是todo上發(fā)生的keyup這個事件术羔,那如果我一直按著某個鍵不放會怎么樣呢赢赊?你的猜測是對的,一直按著的時候级历,數(shù)據(jù)流沒有更新释移,直到你抬起按鍵為止


如果觀察的足夠仔細(xì)的話,你會發(fā)現(xiàn)console中輸出的值其實是 input.target.value寥殖,我們觀察的對象其實是id為todo的這個對象上發(fā)生的keyup事件(Rx.Observable.fromEvent(todo, 'keyup'))玩讳。那么其實在訂閱的代碼段中的input其實是keyup事件才對。好扛禽,我們看看到底是什么锋边,將 console.log(input.target.value) 改寫成 console.log(input),看看會怎樣呢编曼?是的,我們得到的確實是KeyboardEvent


那么我們再來做幾個小練習(xí)剩辟,首先將代碼改成下面的樣子掐场,其實不用我講往扔,你應(yīng)該也可以猜得到,這是要過濾出 keyCode=32 的事件熊户,keyCode是Ascii碼萍膛,那么這就是要把空格濾出來

let todo = document.getElementById('todo');

let input$ = Rx.Observable.fromEvent(todo, 'keyup');

input$

.filter(ev=>ev.keyCode===32)

.subscribe(ev=>console.log(ev.target.value));

結(jié)果我們看到了,按123456789都沒有反應(yīng)嚷堡,直到按了空格


你可能一直在奇怪蝗罗,我們最終只對輸入框的值有興趣,能不能數(shù)據(jù)流只傳值過來呢蝌戒?當(dāng)然可以串塑,使用map這個變換類操作符就可以完成這個轉(zhuǎn)換了

let todo = document.getElementById('todo');

let input$ = Rx.Observable.fromEvent(todo, 'keyup');

input$

.map(ev=>ev.target.value*10)

.subscribe(value=>console.log(value));

map這個操作符做的事情就是允許你對原數(shù)據(jù)流中的每一個元素應(yīng)用一個函數(shù),然后返回并形成一個新的數(shù)據(jù)流北苟,這個數(shù)據(jù)流中的每一個元素都是原來的數(shù)據(jù)流中的元素應(yīng)用函數(shù)后的值桩匪。比如下面的例子,對于原數(shù)據(jù)流中的每個數(shù)應(yīng)用一個函數(shù)10*x友鼻,也就是擴大了10倍傻昙,形成一個新的數(shù)據(jù)流。


最常見的兩個操作符我們上面已經(jīng)了解了彩扔,我們繼續(xù)再來認(rèn)識新的操作符妆档。類似 .map(ev=>ev.target.value) 的場景太多了,以至于rxjs團隊搞出來一個專門的操作符來應(yīng)對虫碉,這個操作符就是 pluck贾惦。這個操作符專業(yè)從事從一系列嵌套的屬性種把值提取出來形成新的流。比如上面的例子可以改寫成下面的代碼蔗衡,效果是一樣的想括。那么如果其中某個屬性為空怎么辦?這個操作符負(fù)責(zé)返回一個 undefined 作為值加入流中瓦堵。

let todo = document.getElementById('todo');

let input$ = Rx.Observable.fromEvent(todo, 'keyup');

input$

.pluck('target', 'value')

.subscribe(value=>console.log(value));

這里解釋下用到的操作符(創(chuàng)建類操作符(from.fromEvent)借帘、變換類操作符(map)、過濾類操作符(filter)济蝉、合并類操作符(combineLatest.zip)杰刽、錯誤處理類操作符、工具類操作符王滤、條件型操作符贺嫂、數(shù)學(xué)和聚集類操作符、連接型操作符等等)

創(chuàng)建類操作符

通常來講雁乡,Rx團隊不鼓勵新手自己從0開始創(chuàng)建Observable第喳,因為狀態(tài)太復(fù)雜,會遺漏一些問題踱稍。Rx鼓勵的是通過已有的大量創(chuàng)建類轉(zhuǎn)換操作符來去建立Observable曲饱。比如 from 和 fromEvent悠抹。

from操作符

from 可以支持從數(shù)組、類似數(shù)組的對象扩淀、Promise楔敌、iterable 對象或類似Observable的對象(其實這個主要指ES2015中的Observable)來創(chuàng)建一個Observable。

這個操作符應(yīng)該是可以創(chuàng)建Observable的操作符中最常使用的一個驻谆,因為它幾乎可以把任何對象轉(zhuǎn)換成Observable卵凑。

var array = [10, 20, 30];

var result$ = Rx.Observable.from(array);

result$.subscribe(x => console.log(x));


fromEvent操作符

這個操作符是專門為事件轉(zhuǎn)換成Observable而制作的,非常強大且方便胜臊。對于前端來說勺卢,這個方法用于處理各種DOM中的事件再方便不過了。

var click$ = Rx.Observable.fromEvent(document, 'click');

click$.subscribe(x => console.log(x));


下面我們稍微給我們的頁面加點料区端,除了輸入框再加一個按鈕

在Javascript中我們同樣方法得到按鈕的DOM對象以及聲明對此按鈕點擊事件的觀察者:

let todo = document.getElementById('todo');

let input$ = Rx.Observable.fromEvent(todo, 'keyup');

input$

.pluck('target', 'value')

.subscribe(value=>console.log(value));

let addBtn = document.getElementById('addBtn');

let buttonClick$ = Rx.Observable.fromEvent(addBtn, 'click');

buttonClick$

.mapTo('clicked');

由于點擊事件沒有什么可見的值值漫,所以我們利用一個操作符叫 mapTo 把對應(yīng)的每次點擊轉(zhuǎn)換成字符 clicked。其實它也是一個 map 的簡化操作织盼。


合并類操作符

combineLatest操作符

既然現(xiàn)在我們已經(jīng)有了兩個流杨何,應(yīng)該試驗一下合并類操作符了,先來試試 combineLatest沥邻,我們合并了按鈕點擊事件的數(shù)據(jù)流和文本框輸入事件的數(shù)據(jù)流危虱,并且返回一個對象,這個對象有兩個屬性唐全,第一個是按鈕事件數(shù)據(jù)流的值埃跷,第二個是文本輸入事件數(shù)據(jù)流的值。也就是說應(yīng)該是類似 { ev: 'clicked', input: '1'} 這樣的結(jié)構(gòu)邮利。

let todo = document.getElementById('todo');

let input$ = Rx.Observable.fromEvent(todo, 'keyup');

// input$

//? .pluck('target', 'value')

//? .subscribe(value=>console.log(value));

let addBtn = document.getElementById('addBtn');

let buttonClick$ = Rx.Observable.fromEvent(addBtn, 'click');

// buttonClick$

//? .mapTo('clicked');

Rx.Observable.combineLatest(buttonClick$, input$, (ev, input)=>{

return {

ev: ev,

input: input

}

})

.subscribe(value => console.log(value))

那看看結(jié)果如何弥雹,在文本輸入框輸入1,沒反應(yīng)延届,再輸入2剪勿,還是沒反應(yīng)


那我們點擊一下按鈕試試,這回有結(jié)果了方庭,但有點沒明白為什么是12厕吉,輸入的數(shù)據(jù)流應(yīng)該是: 1,12械念,... 但那個1怎么丟了呢头朱?


再來文本框輸入3,4看看龄减,這回倒是都出來了


我們來解釋一下combineLatest的機制就會明白了项钮,如下圖所示,上面的2條線是2個源數(shù)據(jù)流(我們分別叫它們源1和源2吧),經(jīng)過combineLatest操作符后產(chǎn)生了最下面的數(shù)據(jù)流(我們稱它為結(jié)果流)寄纵。

當(dāng)源1的數(shù)據(jù)流發(fā)射時鳖敷,源2沒有數(shù)據(jù)脖苏,這時候結(jié)果流也不會有數(shù)據(jù)產(chǎn)生程拭,當(dāng)源2發(fā)射第一個數(shù)據(jù)(圖中A)后,combineLatest操作符做的處理是棍潘,把A和源1的最近產(chǎn)生的數(shù)據(jù)(圖中2)組合在一起恃鞋,形成結(jié)果流的第一個數(shù)據(jù)(圖中2A)。當(dāng)源2產(chǎn)生第二個數(shù)據(jù)(圖中B)時亦歉,源1這時沒有新的數(shù)據(jù)產(chǎn)生恤浪,那么還是用源1中最新的數(shù)據(jù)(圖中2)和源2中最新的數(shù)據(jù)(圖中B)組合。

也就是說 combineLatest 操作符其實是在組合2個源數(shù)據(jù)流中選擇最新的2個數(shù)據(jù)進行配對肴楷,如果其中一個源之前沒有任何數(shù)據(jù)產(chǎn)生水由,那么結(jié)果流也不會產(chǎn)生數(shù)據(jù)。


講到這里赛蔫,有童鞋會問砂客,原理是明白了,但什么樣的實際需求會需要這個操作符呢呵恢?其實有很多鞠值,我這里只舉一個小例子,現(xiàn)在健身這么熱渗钉,比如說我們做一個簡單的BMI計算器彤恶,BMI的計算公式是:體重(公斤)/(身高身高)(米米)。那么我們在頁面給出兩個輸入框和一個用于顯示結(jié)果的div:

那么在JS中鳄橘,我們想要達成的結(jié)果是只有兩個輸入框都有值的時候才能開始計算BMI声离,這時你發(fā)現(xiàn)combineLatest的邏輯不要太順溜啊。

let weight = document.getElementById('weight');

let height = document.getElementById('height');

let bmi = document.getElementById('bmi');

let weight$ = Rx.Observable

.fromEvent(weight, 'input')

.pluck('target', 'value');

let height$ = Rx.Observable

.fromEvent(height, 'input')

.pluck('target', 'value');

let bmi$ = Rx.Observable

.combineLatest(weight$, height$, (w, h) => w/(h*h/100/100));

bmi$.subscribe(b => bmi.innerHTML=b);

zip操作符

除了 combineLatest 瘫怜,Rxjs還提供了多個合并類的操作符术徊,我們再試驗一個 zip 操作符。 zip 和 combineLatest 非常像宝磨,但重要的區(qū)別點在于 zip 嚴(yán)格的需要多個源數(shù)據(jù)流中的每一個的相同順序的元素配對弧关。

比如說還是上面的例子,zip 要求源1的第一個數(shù)據(jù)和源2的第一個數(shù)據(jù)組成一對唤锉,產(chǎn)生結(jié)果流的第一個數(shù)據(jù)世囊;源1的第二個數(shù)據(jù)和源2的第二個數(shù)據(jù)組成一對,產(chǎn)生結(jié)果流的第二個數(shù)據(jù)窿祥。而 combineLatest 不需要等待另一個源數(shù)據(jù)流產(chǎn)生數(shù)據(jù)株憾,只要有一個產(chǎn)生,結(jié)果流就會產(chǎn)生。


zip 這個詞在英文中有拉鏈的意思嗤瞎,記住這個有助于我們理解這個操作符墙歪,就像拉鏈一樣,它需要拉鏈兩邊的齒一一對應(yīng)贝奇。從效果角度上講虹菲,這個操作符有減緩發(fā)射速度的作用,因為它會等待合并序列中最慢的那個掉瞳。

六.拓展思考

七.參考文獻

參考:如何理解Rxjs

參考:RXJS詳解

參考:Angular 從0到1:Rx--隱藏在Angular 中的利劍

參考:通俗的方式理解RxJS

參考:RxJS 核心概念Observer Subscription

參考:30天精通Rxjs

八.更多討論




武漢第149期PPT:鏈接 https://ptteng.github.io/PPT/PPT/JS-10-How-to-understand-RxJS.html#/



武漢第149期視頻連接:視頻https://v.qq.com/x/page/u0517ceol5p.html


_騰訊視頻




------------------------------------------------------------------------------------------------------------------------

技能樹.IT修真院

“我們相信人人都可以成為一個工程師毕源,現(xiàn)在開始,找個師兄陕习,帶你入門霎褐,掌控自己學(xué)習(xí)的節(jié)奏,學(xué)習(xí)的路上不再迷酶昧停”冻璃。

這里是技能樹.IT修真院,成千上萬的師兄在這里找到了自己的學(xué)習(xí)路線损合,學(xué)習(xí)透明化,成長可見化拍埠,師兄1對1免費指導(dǎo)∶奕Γ快來與我一起學(xué)習(xí)吧 分瘾!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市上岗,隨后出現(xiàn)的幾起案子敬锐,更是在濱河造成了極大的恐慌呆瞻,老刑警劉巖痴脾,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件明郭,死亡現(xiàn)場離奇詭異薯定,居然都是意外死亡瞳购,警方通過查閱死者的電腦和手機年堆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來痒蓬,“玉大人,你說我怎么就攤上這事鲁捏「罚” “怎么了动羽?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長盟榴。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么几莽? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮宅静,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘姨夹。我一直安慰自己纤垂,他們只是感情好磷账,可當(dāng)我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布逃糟。 她就那樣靜靜地躺著,像睡著了一般取募。 火紅的嫁衣襯著肌膚如雪斗忌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天聊品,我揣著相機與錄音飞蹂,去河邊找鬼。 笑死翻屈,一個胖子當(dāng)著我的面吹牛陈哑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播伸眶,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼惊窖,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了厘贼?” 一聲冷哼從身側(cè)響起界酒,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎嘴秸,沒想到半個月后毁欣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體庇谆,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年凭疮,在試婚紗的時候發(fā)現(xiàn)自己被綠了饭耳。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡执解,死狀恐怖寞肖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情衰腌,我是刑警寧澤新蟆,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站右蕊,受9級特大地震影響琼稻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜尤泽,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一欣簇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧坯约,春花似錦、人聲如沸莫鸭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽被因。三九已至卿拴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間梨与,已是汗流浹背堕花。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留粥鞋,地道東北人缘挽。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像呻粹,于是被迫代替她去往敵國和親壕曼。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,601評論 2 353

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

  • 第一節(jié):初識Angular-CLI第二節(jié):登錄組件的構(gòu)建第三節(jié):建立一個待辦事項應(yīng)用第四節(jié):進化等浊!模塊化你的應(yīng)用第...
    接灰的電子產(chǎn)品閱讀 15,573評論 31 77
  • 介紹 RxJS是一個異步編程的庫腮郊,同時它通過observable序列來實現(xiàn)基于事件的編程。它提供了一個核心的類型:...
    泓滎閱讀 16,600評論 0 12
  • 作者: maplejaw本篇只解析標(biāo)準(zhǔn)包中的操作符筹燕。對于擴展包轧飞,由于使用率較低衅鹿,如有需求,請讀者自行查閱文檔过咬。 創(chuàng)...
    maplejaw_閱讀 45,661評論 8 93
  • title: RxJS簡介date: 2017-08-01 09:45:33tags: [JavaScript, ...
    color_cat閱讀 293評論 0 0
  • 版權(quán)聲明:本文為小斑馬偉原創(chuàng)文章大渤,轉(zhuǎn)載請注明出處! 上篇簡單的闡述了響應(yīng)式編程的基本理論援奢。這篇主要對響應(yīng)編程進行詳...
    ZebraWei閱讀 2,314評論 0 2