Rxjs的flatMap使用
flatMap是Rxjs比較繞的一個概念,這里我們只是講解如何使用。在Rxjs 4.0版本時叫flatMap,在Rxjs 5.0時被更名為margeMap,現在flatMap作為margeMap的別名使用,這是考慮向下兼容。
官方flatMap的定義:
Projects each source value to an Observable which is merged in the output Observable.
mergeMap(project: function(value: T, ?index: number): ObservableInput, resultSelector: function(outerValue: T, innerValue: I, outerIndex: number, innerIndex: number): any, concurrent: number): Observable
名稱 | 類型 | 屬性 | 描述 |
---|---|---|---|
project | function(value: T, ?index: number): ObservableInput | 函數,當應用于源 Observable 發出的項時,返回一個 Observable 。 | |
resultSelector | function(outerValue: T, innerValue: I, outerIndex: number, innerIndex: number): any | 可選的 | 函數,它用于產生基于值的輸出 Observable 和源(外部)發送和內部 Observable 發送的索引。傳遞給這個函數參數有: outerValue: 來自源的值 innerValue:來自投射的Observable 的值 outerIndex: 來自源的值的 “index” innerIndex: 來自投射的 Observable 的值的 “index” |
concurrent | number | 可選的,默認值: Number.POSITIVE_INFINITY | 可以同時訂閱的輸入 Observables 的最大數量。 |
返回:
Observable
該 Observable 發出由源 Observable 發出的每項應用投射函數 (和可選的 resultSelector)后的結果,并合并從該轉化獲得的 Observables 的結果。
Demo
var source = Rx.Observable.of(1,2,3).flatMap(function (x, i) {console.log(`x:${x},i:${i}`);return [x, i];},function (x, y, ix, iy) { console.log(`x:${x},y:${y},ix:${ix},iy:${iy}`);return x + y + ix + iy;});
var subscription = source.subscribe(function (x) { console.log(x);});
JSBin運行:
"x:1,i:0"
"x:1,y:1,ix:0,iy:0"
2
"x:1,y:0,ix:0,iy:1"
2
"x:2,i:1"
"x:2,y:2,ix:1,iy:0"
5
"x:2,y:1,ix:1,iy:1"
5
"x:3,i:2"
"x:3,y:3,ix:2,iy:0"
8
"x:3,y:2,ix:2,iy:1"
8
分享一下流程:
- flatMap將1,2,3
數據流一次傳入flatMap的第一個回調函數,然后輸出”x:1,i:0”
- 然后返回[1,0]數組給第二個回調函數,這里的1是value,0是index,是數據的索引號
- 輸出”x:1,y:1,ix:0,iy:0”,然后返回2,這里的x參數是outerValue,y是innerValue,ix,是outerIndex,iy是innerIndex
- 在訂閱的回調函數中輸出2
- 其中的innerValue回退到上一次的值,對應的索引增加,此時值為”x:1,y:0,ix:0,iy:1”,然后返回2
- 在訂閱的回調函數中輸出2
- 接下來flatMap接受值2,然后傳入第一個回調函數project,”x:2,i:1”,返回[2,1]
- 將[2,1]傳入第二個回調函數resultSelector,輸出”x:2,y:2,ix:1,iy:0”,返回5
- 在訂閱的回調函數中輸出5
- 然后resultSelector回調函數又一次被調用,傳入的參數(2,1,1,1),輸出”x:2,y:1,ix:1,iy:1”,并返回5
- 在訂閱的回調函數中輸出5
下面依次類推,注意的一點是,resultSelector函數會被調用兩次,第一次innerValue為當前投射的值,第二次innerValue為上一次的值。
為了驗證我們的想法將flatMap的project函數返回值為[x]:
var source = Rx.Observable.of(1,2,3).flatMap(function (x, i) {console.log(`x:${x},i:${i}`);return [x];},function (x, y, ix, iy) { console.log(`x:${x},y:${y},ix:${ix},iy:${iy}`);return x + y + ix + iy;});
var subscription = source.subscribe(function (x) { console.log(x);});
輸出結果:
"x:1,i:0"
"x:1,y:1,ix:0,iy:0"
2
"x:2,i:1"
"x:2,y:2,ix:1,iy:0"
5
"x:3,i:2"
"x:3,y:3,ix:2,iy:0"
8
根據結果我們能夠發現resultSelector
函數的執行次數取決于project的返回值。那么我們繼續修改返回值為[x,1]
var source = Rx.Observable.of(1,2,3).flatMap(function (x, i) {console.log(`x:${x},i:${i}`);return [x,1];},function (x, y, ix, iy) { console.log(`x:${x},y:${y},ix:${ix},iy:${iy}`);return x + y + ix + iy;});
var subscription = source.subscribe(function (x) { console.log(x);});
輸出結果:
"x:1,i:0"
"x:1,y:1,ix:0,iy:0"
2
"x:1,y:1,ix:0,iy:1"
3
"x:2,i:1"
"x:2,y:2,ix:1,iy:0"
5
"x:2,y:1,ix:1,iy:1"
5
"x:3,i:2"
"x:3,y:3,ix:2,iy:0"
8
"x:3,y:1,ix:2,iy:1"
7
結論:
根據這個結果我們能夠總結resultSelector
函數的y
值取決于[x,i]
中的i
,如果i為0,那么y的值第一次等于x,然后不發生變化。如果i
為1那么y第一次等于x,然后需要回退到當前i的值。如果i
為2,那么y會從當前的x變化到i
,這期間iy的值會從0變化為1。
千萬不要高興的太早,剛剛的結論我們只是嘗試著去總結,但是不幸的是錯的,沒錯,比如我們做如下修改
var source = Rx.Observable.of(1,2,3).flatMap(function (x, i) {console.log(`x:${x},i:${i}`);return [2, i];}, // 修改這里function (x, y, ix, iy) { console.log(`x:${x},y:${y},ix:${ix},iy:${iy}`);return x + y + ix + iy;});
var subscription = source.subscribe(function (x) { console.log(x);});
返回結果
"x:1,i:0"
"x:1,y:2,ix:0,iy:0"
3
"x:1,y:0,ix:0,iy:1"
2
"x:2,i:1"
"x:2,y:2,ix:1,iy:0"
5
"x:2,y:1,ix:1,iy:1"
5
"x:3,i:2"
"x:3,y:2,ix:2,iy:0"
7
"x:3,y:2,ix:2,iy:1"
8
這個結果說明什么呢,project返
回的數組決定resultSelector
回調函數中的y
,而resultSelector
回調函數中的x
是不受影響的,取決于外界傳入的值。當project
返回的數組中[2,i]
,決定y
的觸發次數,如果有兩個數組元素就被觸發2次,有5個數組元素那么就被觸發5次。
最終結論:
resultSelector
project
回調函數的返回值決定resultSelector
回調函數的參數y
即innerValue,同時也決定resultSelector
回調函數被觸發幾次。
如果將flatMap的第二個回調函數去除
var source = Rx.Observable.of(1,2,3).flatMap(function (x, i) {console.log(`x:${x},i:${i}`);return [x, i];});
var subscription = source.subscribe(function (x) { console.log(x);});
JSBin結果
"x:1,i:0"
1
0
"x:2,i:1"
2
1
"x:3,i:2"
3
2
我們可以看到這是正常的結果,flatMap返回Observable的[x,i]數組。