处理高阶流(high order observable)

所谓高阶流就是指一个 observable 送出的元素还是一个 observable, 像是二维矩阵一样,通常我们需要的元素是第二层的 observable 送出的元素, 所以我们希望可以把二维的 observable 变成一维的

switch

在新的 observable 送出后直接处理新的 observable 不管前一个 observable 是否完成,每当有新的 observable 送出就会直接把旧的 observable 退订(unsubscribe),永遠只处理最新的 observable!

concatAll

concatAll 最重要的点就是他会处理完前一个 observable 才會在处理下一个observable, 串行处理. 也就是一定是等前一个 observable 完成后才会处理下一个

mergeAll

对所有的 observable 并行处理

mergeAll 还可以传入一个数值, 代表他可以同时处理的 observable 的数量

如果数量设置了2 ,前两个流是并行处理的, 第三个流会等第一个流结束了之后才会开始执行

concatMap

concatMap 其实是 concatAll 加上 map 的简易写法, 🌰如下:

1
2
const source = Rx.Observable.fromEvent(document.body, 'click')
const example = source.map(e => Rx.Observable.interval(1000).take(3)).concatAll()

上面这个示例就可以简化成

1
2
3
4
const source = Rx.Observable.fromEvent(document.body, 'click')
const example = source.concatMap(e =>
Rx.Observable.interval(1000).take(3)
)
1
2
3
4
// Marble Diagram
source : -----------c--c------------------...
concatMap(c => Rx.Observable.interval(100).take(3))
example: -------------0-1-2-0-1-2---------...

这样的行为也很常用在发送 request 的时候:

1
2
3
4
5
6
function getPostData() {
return fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(res => res.json())
}
const source = Rx.Observable.fromEvent(document.body, 'click')
const example = source.concatMap(e => Rx.Observable.from(getPostData()))

这样我们每次点击就会送出一个 http request, 如果我们快速连续点击, 可以在开发者工具的 network 看到每个 request 都是等到前一个 request 完成后才送出下一个 request 的

concatMap 的第二个参数是一个 callback, 这个 callback 会传入四个参数, 分别是

  1. 外部 observable 送出的元素
  2. 内部 observable 送出的元素
  3. 外部 observable 送出元素的 index
  4. 内部 observable 送出元素的 index

用来传回我们想要的值

switchMap

switchMap 是 switch 加上 map 的简易写法

很适合用在只看最后一个 request 的情景, 比如说输入文字自动完成的情景, 我们只需要显示使用者最后一次打在页面上的文字来进行建议选项,而不用每一次都发送一次请求

switchMap 的第二个参数的行为和 concatMap 一样,这里不再赘述

mergeMap

mergeMap 也能传入第二个参数, 跟上面讲的 switchMap 和 concatMap 的第二个参数的行为也是完全一样的, 但是 mergeMap 的重点是可以传入第三个参数来限制并行处理的数量

switchMap, mergeMap, concatMap

这三个 operators 还有一个共同点的特性就是可以吧第一个参数所返回的 promise对象直接转成 observable, 这样我们就不需要在用Rx.Observable.from来转换一次

1
2
3
4
5
6
function getPostData() {
return fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(res => res.json())
}
const source = Rx.Observable.fromEvent(document.body, 'click')
const example = source.concatMap(e => getPostData()) // 直接传回 promise

把一般流转换成高阶流

window

window 总共有五个相关的 operator

  • window
  • windowCount
  • windowTime
  • windowToggle
  • windowWhen

window 与 buffer 类似 , 可以把一段时间内送出的元素拆分出来

buffer 是根据传入的 source2去把 source 的元素拆分合并到数组中

1
2
3
4
5
6
7
source.buffer(source2)
/* Marble Diagram
source : --0--1--2--3--4--5--6--7..
source2: ---------0---------1--------...
buffer(source2)
example: ---------([0,1,2])---------([3,4,5])
*/

source.window(source2)

而 window 则是根据 source 把 source2 的元素拆分合并到新的 observable

看一个栗子:

1
2
3
4
5
6
const click = Rx.Observable.fromEvent(document, 'click')
const source = Rx.Observable.interval(1000)
const example = source.window(click)
example.map(innerObservable => innerObservable.count())
.switch()
.subscribe(console.log)

Marble Diagram

1
2
3
4
5
6
7
8
9
10
11
12
source : ---------0---------1---------2--...
click : --cc---cc----c-c----------------...
window(source)
example: o--------o---------o---------o--..
\ \ \ \
-cc---cc|---c-c---|---------|--..
count()
: o--------o---------o---------o--
\ \ \ \
-------4|--------2|--------0|--..
switch()
: ---------4---------2---------0--...

windowToggle

windowToggle 不像 window 只能控制内部 observable 的结束, windowToggle 可以传入两个参数, 第一个是开始的 observable, 第二个是一个 callback 可以回传一个结束的 observable, 🌰:

1
2
3
4
5
6
7
8
9
const source = Rx.Observable.interval(1000);
const mouseDown = Rx.Observable.fromEvent(document, 'mousedown');
const mouseUp = Rx.Observable.fromEvent(document, 'mouseup');

const example = source
.windowToggle(mouseDown, () => mouseUp)
.switch();

example.subscribe(console.log);

Marble Diagram

1
2
3
4
5
6
7
8
9
10
11
12
source   : ----0----1----2----3----4----5--...

mouseDown: -------D------------------------...
mouseUp : ---------------------------U----...

windowToggle(mouseDown, () => mouseUp)

: -------o-------------------------...
\
-1----2----3----4--|
switch()
example : ---------1----2----3----4---------...