非同步最難的地方在於,當有多個非同步行爲同時觸發且相互依賴,這時候咱們要處理的邏輯跟狀態就會變得極其複雜,甚至程序極可能會在完成的一兩天後就成了 Legacy Code(遺留代碼)。javascript
昨天咱們最後講到了 merge
的用法,它的邏輯就像是 OR(||)同樣,能夠把多個 observable 合而且同時處理,當其中任合一個 observable 送出元素時,咱們都作相同的處理。java
今天咱們要講的三個 operators 則像是 AND(&&) 邏輯,它們都是在多個元素送進來時,只輸出一個新元素,但各自的行爲上仍有差別,須要讀者花點時間思考,建議在頭腦清醒時閱讀本篇文章。編輯器
首先咱們要介紹的是 combineLatest,它會取得各個 observable 最後送出的值,再輸出成一個值,咱們直接看示例會比較好解釋。ui
var source = Rx.Observable.interval(500).take(3);
var newest = Rx.Observable.interval(300).take(6);
var example = source.combineLatest(newest, (x, y) => x + y);
example.subscribe({
next: (value) => { console.log(value); },
error: (err) => { console.log('Error: ' + err); },
complete: () => { console.log('complete'); }
});
// 0
// 1
// 2
// 3
// 4
// 5
// 6
// 7
// complete複製代碼
你們第一次看到這個 output 應該都會很困惑,咱們直接來看 Marble Diagram 吧!.net
source : ----0----1----2|
newest : --0--1--2--3--4--5|
combineLatest(newest, (x, y) => x + y);
example: ----01--23-4--(56)--7|複製代碼
首先 combineLatest
能夠接收多個 observable,最後一個參數是 callback function,這個 callback function 接收的參數數量跟合併的 observable 數量相同,依照示例來講,由於咱們這裏合併了兩個 observable 因此後面的 callback function 就接收 x, y 兩個參數,x 會接收從 source 發送出來的值,y 會接收從 newest 發送出來的值。code
最後一個重點就是必定會等兩個 observable 都曾有送值出來纔會呼叫咱們傳入的 callback,因此這段程式是這樣運行的ip
0
,但此時 source 並無送出過任何值,因此不會執行 callback0
,此時 newest 最後一次送出的值爲 0
,把這兩個數傳入 callback 獲得 0
。1
,此時 source 最後一次送出的值爲 0
,把這兩個數傳入 callback 獲得 1
。2
,此時 source 最後一次送出的值爲 0
,把這兩個數傳入 callback 獲得 2
。1
,此時 newest 最後一次送出的值爲 2
,把這兩個數傳入 callback 獲得 3
。3
,此時 source 最後一次送出的值爲 1
,把這兩個數傳入 callback 獲得 4
。2
,此時 newest 最後一次送出的值爲 3
,把這兩個數傳入 callback 獲得 5
。4
,此時 source 最後一次送出的值爲 2
,把這兩個數傳入 callback 獲得 6
。5
,此時 source 最後一次送出的值爲 2
,把這兩個數傳入 callback 獲得 7
。不論是 source 仍是 newest 送出值來,只要另外一方曾有送出過值(有最後的值),就會執行 callback 並送出新的值,這就是 combineLatest。內存
combineLatest 很經常使用在運算多個因子的結果,例如最多見的 BMI 計算,咱們身高變更時就拿上一次的體重計算新的 BMI,當體重變更時則拿上一次的身高計算 BMI,這就很適合用 combineLatest 來處理!get
在講 withLatestFrom 以前,先讓咱們先來看一下 zip 是怎麼運行的,zip 會取每一個 observable 相同順位的元素並傳入 callback,也就是說每一個 observable 的第 n 個元素會一塊兒被傳入 callback,這裏咱們一樣直接用示例講解會比較清楚
var source = Rx.Observable.interval(500).take(3);
var newest = Rx.Observable.interval(300).take(6);
var example = source.zip(newest, (x, y) => x + y);
example.subscribe({
next: (value) => { console.log(value); },
error: (err) => { console.log('Error: ' + err); },
complete: () => { console.log('complete'); }
});
// 0
// 2
// 4
// complete複製代碼
Marble Diagram 長這樣
source : ----0----1----2|
newest : --0--1--2--3--4--5|
zip(newest, (x, y) => x + y)
example: ----0----2----4|複製代碼
以咱們的示例來講,zip 會等到 source 跟 newest 都送出了第一個元素,再傳入 callback,下次則等到 source 跟 newest 都送出了第二個元素再一塊兒傳入 callback,因此運行的步驟以下:
0
,但此時 source 並無送出第一個值,因此不會執行 callback。0
,newest 以前送出的第一個值爲 0
,把這兩個數傳入 callback 獲得 0
。1
,但此時 source 並無送出第二個值,因此不會執行 callback。2
,但此時 source 並無送出第三個值,因此不會執行 callback。1
,newest 以前送出的第二個值爲 1
,把這兩個數傳入 callback 獲得 2
。3
,但此時 source 並無送出第四個值,因此不會執行 callback。2
,newest 以前送出的第三個值爲 2
,把這兩個數傳入 callback 獲得 4
。zip 會把各個 observable 相同順位送出的值傳入 callback,這很常拿來作 demo 使用,好比咱們想要間隔 100ms 送出 'h', 'e', 'l', 'l', 'o',就能夠這麼作
var source = Rx.Observable.from('hello');
var source2 = Rx.Observable.interval(100);
var example = source.zip(source2, (x, y) => x);複製代碼
這裏的 Marble Diagram 就很簡單
source : (hello)|
source2: -0-1-2-3-4-...
zip(source2, (x, y) => x)
example: -h-e-l-l-o|複製代碼
這裏咱們利用 zip 來達到本來只能同步送出的資料變成了非同步的,很適合用在創建示範用的資料。
建議你們日常沒事不要亂用 zip,除非真的須要。由於 zip 必須 cache 住還沒處理的元素,當咱們兩個 observable 一個很快一個很慢時,就會 cache 很是多的元素,等待比較慢的那個 observable。這頗有可能形成內存相關的問題!
withLatestFrom 運行方式跟 combineLatest 有點像,只是他有主從的關係,只有在主要的 observable 送出新的值時,纔會執行 callback,附隨的 observable 只是在背景下運行。讓咱們看一個例子
var main = Rx.Observable.from('hello').zip(Rx.Observable.interval(500), (x, y) => x);
var some = Rx.Observable.from([0,1,0,0,0,1]).zip(Rx.Observable.interval(300), (x, y) => x);
var example = main.withLatestFrom(some, (x, y) => {
return y === 1 ? x.toUpperCase() : x;
});
example.subscribe({
next: (value) => { console.log(value); },
error: (err) => { console.log('Error: ' + err); },
complete: () => { console.log('complete'); }
});複製代碼
先看一下 Marble Diagram
main : ----h----e----l----l----o|
some : --0--1--0--0--0--1|
withLatestFrom(some, (x, y) => y === 1 ? x.toUpperCase() : x);
example: ----h----e----l----L----O|複製代碼
withLatestFrom 會在 main 送出值的時候執行 callback,但請注意若是 main 送出值時 some 以前沒有送出過任何值 callback 仍然不會執行!
這裏咱們在 main 送出值時,去判斷 some 最後一次送的值是否是 1 來決定是否要切換大小寫,執行步驟以下
h
,此時 some 上一次送出的值爲 0
,把這兩個參數傳入 callback 獲得 h
。e
,此時 some 上一次送出的值爲 0
,把這兩個參數傳入 callback 獲得 e
。l
,此時 some 上一次送出的值爲 0
,把這兩個參數傳入 callback 獲得 l
。l
,此時 some 上一次送出的值爲 1
,把這兩個參數傳入 callback 獲得 L
。o
,此時 some 上一次送出的值爲 1
,把這兩個參數傳入 callback 獲得 O
。withLatestFrom 很經常使用在一些 checkbox 型的功能,例如說一個編輯器,咱們開啓粗體後,打出來的字就都要變粗體,粗體就像是 some observable,而咱們打字就是 main observable。
今天介紹了三個合併用的 operators,這三個 operators 的 callback 都會依照合併的 observable 數量來傳入參數,若是咱們合併了三個 observable,callback 就會有三個參數,而無論合併幾個 observable 都會只會回傳一個值。
這幾個 operators 須要花比較多的時間思考,讀者們不用硬記他的運行行爲,只要稍微記得有這些 operators 能夠用就能夠了。等到真的要用時,再從新回來看他們的運行方式作選擇。
不知道讀者們今天有沒有收穫呢? 若是有任何問題,歡迎在下方留言給我,謝謝!