Web 平台 API / RxJS · 从 Observable 标准契约,到它解决的那些问题 / 变换:pipe 把 operator 串成管道 待审核 4 / 7
operators · pipe / map / filter / scan / take

变换:pipe 把 operator 串成管道

契约一页里,一条 Observable 只会把源头的值原样推出去。真正的表达力来自 operator:每个 operator 都是一个纯函数,吃一个上游 Observable、返回一个全新的 Observable,从不改动上游。pipe(a, b, c) 只是把这些纯函数组合起来,数据自上而下逐节流过。上游不知道下游是谁,下游也拿不到上游的内部状态,只看得到流出来的 marble。

1 · 单个 operator 的逐值变换

源流 source$ 固定在 t=1..6 依次推 1,2,3,4,5,6t=7complete 封口。换一个 operator,就用它把 source$ 变换成一条新的 result$,而 source$ 那条轴自始至终不变——这就是不可变。

图 1-1 · 同一条源流经不同 operator 后的下游。可换 operator 并推进时间游标,同一时刻上下游一起点亮,看每颗 marble 如何被改写、丢弃或改数。

2 · pipe 的三节串联

operator 是纯函数,组合它们就是函数组合。下面这条 pipe 把三节接在一起:先 filter 只留偶数,再 map 把值乘十,最后 take(2) 取够两个就 complete。逐节推演下来,偶数 2, 4, 6filter 留下,map 乘十成 20, 40, 60,而 take(2) 在数够两个的那一刻就 complete——所以整条流在 t=4 提前收尾,60 根本不会流出来。

图 2-1 · 源、filter 后、map 后、take 后四条时间轴并排。可推进时间游标,看同一颗值怎样被下一节接手,以及 take 满员后如何连同后续一起截断。

建议 · 每个 operator 只读上游、产出一条新流,绝不回头改上游,所以同一个 source$ 可以被多条不同的 pipe 各自消费而互不干扰,和数组的 map / filter 返回新数组是同一种思路。也正因为 operator 只是「Observable 到 Observable」的函数,pipe 才能像搭积木一样自由拼接。

map 的返回值本身又是一个 Observable(比如每个 id 去发一个请求),就会得到「流的流」,这时要靠展平 operator 把内层摊平。