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]数组。