RxJS 系列 – Transformation Operators
前言
前几篇介绍过了
这篇继续介绍 Transformation Operators.
参考
Docs – Transformation Operators

map
就是 Array 的 map 咯
const obs = from([1, 2, 3, 4]);
obs.pipe(map(v => v + 10)).subscribe(v => console.log(v)); // 11..12..13..14
scan
scan 相等于 Array 的 reduce
const obs = from([1, 2, 3, 4]);
obs
.pipe(
scan((acc, value, _index) => {
return acc + value;
}, 0)
)
.subscribe(v => console.log(v)); // 1..3..6..10
每一次 acc 代表上一次 return value
value 表示 obs 这一次的值
而第一次发布的时候, 由于没有上一次, acc 的值将是 init value (也就是 scan 的第二个参数)
pairwise
pairwise 是 "一对" 的概念, 它每次接收都是 2 个值, 当前值和上一次值
const obs = from([1, 2, 3, 4]);
obs.pipe(pairwise()).subscribe(v => console.log(v)); // [1,2]..[2,3]..[3,4]
由于第一次发布时, 没有 "上一次值", 所以不会接收, 上面例子中, 发布了 4 次, 但是接收只有 3 次.
concatMap
之前介绍过 concat, 把一堆 Observables 丢给它, 它会从第一个开始 subscribe 直到那一个 Observable complete 后再去 subscribe 下一个 Observable, 直到那一堆 Observable 全部结束.
concat(o1, o2, o3, o4).subscribe()
concatMap 和 concat 概念一样都是 complete 后去 subscribe 下一个. 不同地方在于那一堆 Observables 提供的方式
const obs = from([1, 2, 3, 4]);
obs.pipe(concatMap(v => of(v))).subscribe(v => console.log(v));
concatMap() 接收 obs 的值, 然后返回 Observable. 上面例子中 obs 发布 4 次. concatMap 也就发布 4 个 Observables
而这 4 个 Observables 就被 concat(o1, o2, o3, o4) 了.
所以你也能把它理解为一种动态的 concat. 因为 concat(observables) 是初始化就决定了多少个 observables 被放进去.
而 concatMap 则是一个一个添加进去的.
mergeMap
理解了 concatMap 就理解了 mergeMap. 它就是动态的 merge.
merge 的特点是, 它不像 concat 那样会等待 complete. 它会直接 subscribe 所以的 Observables.
switchMap
没有 switch 只有 switchMap.
switchMap 的接口和 concatMap, mergeMap 一样, 接收 obs 的值, 并且返回一个 Observable
concatMap 和 mergeMap 会把返回的 Observable 堆叠起来 (concat 挨个挨个 subscribe, merge 直接 subscribe all)
但 switchMap 不会把 Observable 堆叠起来, 它会 subscribe Observable, 一旦有下一个 Observable, 它会 unsubscribe 上一个 Observable (丢弃它), 然后 subscribe 下一个 Observable.
这也是为什么没有 switch 只有 switchMap, 因为它只有一个 Observable 不像 merge 和 concat 有一堆 Observable.
const obs = fromEvent(document, 'click');
obs
.pipe(switchMap(v => fetch('https://random-data-api.com/api/v2/users').then(r => r.json())))
.subscribe(v => console.log(v));
每次点击就会发 ajax, 如果点击很快, ajax 还没有返回, 那么会放弃上一次的 ajax 请求, 马上在发新的 ajax.
exhaustMap
switchMap 和 exhaustMap 的关系有点类似 debounceTime 和 throttleTime 的关系.
debounceTime 的特色是 delay and keep postpone
throttleTime 的特色是 immediately + skip
switchMap 有新 Observable 它就会丢弃旧的, subscribe 新的. 这样连续就会导致 subscriber 一直接收不到值. 这个就像 debounceTime 的 keep postpone.
exhaustMap 则像 throttleTime, 有 Observable 后它就 subscribe. 在 Observable 没有 complete 前 (注: 一定要 complete 哦, next 不够), 它无视接下来每一个新的 Observables.
switchScan
switchScan 和 swtichMap 概念差不多, 只是引入了 scan 的概念.
const obs = fromEvent(document, 'click');
obs
.pipe(
switchScan((acc, _value, _index) => {
return timer(2000).pipe(map(() => acc + 1));
}, 0)
)
.subscribe();
第 1 秒 click, acc = 0 (初始值)
第 2 秒 click, acc = 0 因为返回的 Observable 需要 2 秒, 而 click 太快了, swtich 的概念就是放弃之前的, 拥抱新的.
第 4 秒 click, acc = 1
第 6 秒 click, acc = 2
concatMap, switchMap, mergeMap, exhaustMap 小结
参考: RxJS 轉換類型 Operators (2) - switchMap / concatMap / mergeMap / exhaustMap
bufferTime
bufferTime 的作用是把 Observable 发布的值缓存起来, 等时机到了一次接收
const obs = timer(0, 1000);
obs.pipe(bufferTime(3000)).subscribe(values => console.log(values));
效果

obs 每秒发布一个值, 经过 bufferTime 会被缓存起来, 直到每 3 秒 bufferTime 发布, console 才会接收到 3 秒内缓存的所以值.
效果图中, 第 3 次接收 4 个 values 是因为计时有微差.
bufferCount
和 bufferTime 一样都是先缓存, 然后在一起发布. 区别是它不是以时间来计算, 它以数量做计算.
const obs = timer(0, 1000);
obs.pipe(bufferCount(3)).subscribe(values => console.log(values));
效果

缓存满 3 个就一起发布.
startBufferEvery
bufferCount 还有第二个参数叫 startBufferEvery, 虽然冷门, 但也可以了解一下.

它的玩法是这样的
const obs = interval(1000);
obs
.pipe(bufferCount(3, 2))
.subscribe(values => console.log((performance.now() / 1000).toFixed(0) + 's', values));
bufferCount(3, 2) 表示每当 obs 发布 2 次, subscrube 就接收一次包含 3 个值
效果

2 个点要注意.
一, 2, 4 值重复了, 因为要求每 2 次发布就要接收 3 个值, 那就差了一个, 于是就拿之前的补上.
二, 第一次发布等待了 3 秒, 第二次则是 2 秒, 因为每一次要求 3 个 值, 而第一次 2 秒钟收集时并不满足要求, 而一到 3 秒中满足了需求就立刻发布.
buffer
buffer 的区别是, 我们可以完全控制什么时机发布. 不只是 by time or by count, 可以 by whatever
const obs = timer(0, 1000);
obs.pipe(buffer(fromEvent(document, 'click'))).subscribe(values => console.log(values));
当 document click 的时候把缓存一并发布.
bufferWhen
参考: Stack Overflow – What's the difference between the RxJS operators "buffer" and "bufferWhen"?
bufferWhen 和 buffer 有一个小区别. buffer 的参数是一个 Observable
bufferWhen 的参数是一个 () => Observable
obs
.pipe(
bufferWhen(
() =>
new Observable(subscriber => {
console.log('Observable Init');
document.addEventListener('click', () => {
subscriber.next();
});
return () => {
console.log('Observable Displose');
};
})
)
)
.subscribe(values => console.log(values));
buffer 参数 Observable 只会被 subscribe 一次.
bufferWhen 参数返回的 Observable 在每一次原 obs 一并发布后都会被 resubscribe
上面代码的效果是这样的

bufferToggle
上面的 buffer operators 都只有一个 "发布时机".
bufferToggle 有 2 个时机, 一个是 "发布时机", 另一个是其它 buffer operators 没有的 "开始缓存时机".

参数 1 是开始缓存, 参数 2 是发布.
const obs = timer(0, 1000);
obs
.pipe(bufferToggle(fromEvent(document, 'click'), () => fromEvent(document, 'contextmenu')))
.subscribe(values => console.log(values));
效果

我在第 3 秒的时候 click 了一下, 第 6 秒 right click 了一下, 所以得到了 [3, 4, 5]
又在 第 10 秒 click 了一下, 第 13 秒 right click 了一下, 得到了 [10, 11, 12]
这就是所谓的, 控制开始和发布时机. 不在时机内的值将会丢失. 像 0, 1, 2, 6, 7, 8, 9 都接收不到.
buffer operators 小结
参考: 30 天精通 RxJS (12): Observable Operator - scan, buffer
常用到的是 bufferTime, bufferCount, buffer.
bufferWhen 和 bufferToggle 我目前都没有用过.
windowTime, windowCount, window, windowToggle, windowWhen
参考:
RxJS window() Transformation Operator
Stack Overflow – What does the `window` mean in RxJS?
30 天精通 RxJS(20): Observable Operators - window, windowToggle
window 和 buffer 基本上是一样的. 唯一的区别是 buffer 接收的是 Array. window 接收的是 Observable (类似于 from(Array))
我们来看一个对比, 感受一下它们的区别
const obs = timer(0, 1000);
obs.pipe(bufferTime(10000)).subscribe(values => console.log(values));
10 秒钟后会接收到 Array [0, 1, 2 ... 10]
换成 windowTime
const obs = timer(0, 1000);
obs
.pipe(
windowTime(10000),
switchMap(v => v)
)
.subscribe(values => console.log(values));
1 秒钟后就会接收到 value 1
一个 10 秒后才接收, 一个第 1 秒就开始接收了, 这就是所谓的 immediately

那什么时候用 window 什么时候用 buffer 呢? 我不清楚, 但我自己的经验是绝大部分情况下用 buffer 就够了.
groupBy
和 array 的 groupBy 一个概念
const obs = new Observable<{ name: string; age: number }>(subscriber => {
subscriber.next({ name: 'dada', age: 1 });
subscriber.next({ name: 'derrick', age: 2 });
subscriber.next({ name: 'dada', age: 3 });
subscriber.next({ name: 'derrick', age: 4 });
});
obs.pipe(groupBy(v => v.name)).subscribe(g => {
g.subscribe(v => console.log(g.key, v));
});
当 obs 发布新值的时候, groupBy 会依据 key 查看之前是否有创建过 GroupedObservable
如果没有就创建新的, 并且发布下去. 所以 subscribe 接收到的是 GroupedObservable 哦.
如果已经创建过了, 那么它就 .next 把值传下去.
上面的例子中, subscribe 会接收 2 次, 一个是 dada 的 GroupedObservable 另一个是 derrick 的 GroupedObservable
订阅这些 Observable 就可以获取到每次发布的对象了.
我在项目中没有用过 groupBy 一时也想不到什么情况可能会用到它.
没有介绍到的 Transformation Operators
expand
mergeScan
废弃了的 Transformation Operators
mapTo
switchMapTo
concatMapTo
mergeMapTo
pluck
partition
exhaust
一句话总结
map : Array map
scan : Array reduce
pairwise : 每次接收 “一对”, [prev value, curr value]
concatMap : concat(动态 Observables)
mergeMap : merge(动态 Observables)
switchMap : 自动 subscribe map 返回的 Observable, 当有新的 Observable 自动 unsubscribe 上一个 Observable
exhaustMap : switchMap unsubscribe old + subscribe new, exhaustMap subscribe old + skip new (until old complete)
switchScan : switchMap 引入 scan 概念
bufferTime : 缓存值, 到时机一并发送 array
bufferCount : 缓存到量, 一并发送
buffer : 用 notification observable 控制一并发送时机
bufferToggle : 控制开始缓存时机和发送时机
bufferWhen : notification observable 会被 resubscribe, buffer 不会
window operators : 和 buffer 一样, 唯一区别是它不接收 Array 而是 Observable 类似于 from(Array), 还有, 接收的时机不同, buffer 是等 array 满了才接收, window 是一开始就持续接收直到 complete.
groupBy : 把值进行分组, 发布 GroupedObservable
RxJS 系列 – Transformation Operators的更多相关文章
- [RxJS] Transformation operators: debounce and debounceTime
Debounce and debounceTime operators are similar to delayWhen and delay, with the difference that the ...
- [RxJS] Transformation operators: delay and delayWhen
This lessons teaches about delay and delayWhen: simple operators that time shift. delay(number | dat ...
- RxJS——调度器(Scheduler)
调度器 什么是调度器?调度器是当开始订阅时,控制通知推送的.它由三个部分组成. 调度是数据结构.它知道怎样在优先级或其他标准去存储和排队运行的任务 调度器是一个执行上下文.它表示任务在何时何地执行(例 ...
- rxjs笔记(未完成)
首先是 Observable 和promise的区别, 1返回值个数,Observable 可以返回0到无数个值. 2.Promise主动推送,控制着"值"何时被 "推送 ...
- [RxJS] Split an RxJS observable with window
Mapping the values of an observable to many inner observables is not the only way to create a higher ...
- RxJS入门2之Rxjs的安装
RxJS V6.0+ 安装 RxJS 的 import 路径有以下 5 种: 1.创建 Observable 的方法.types.schedulers 和一些工具方法 import { Observa ...
- Angular 4+ 修仙之路
Angular 4.x 快速入门 Angular 4 快速入门 涉及 Angular 简介.环境搭建.插件表达式.自定义组件.表单模块.Http 模块等 Angular 4 基础教程 涉及 Angul ...
- Angular5的new feature
https://blog.angular.io/version-5-0-0-of-angular-now-available-37e414935ced Version 5.0.0 of Angular ...
- flink学习笔记:DataSream API
本文为<Flink大数据项目实战>学习笔记,想通过视频系统学习Flink这个最火爆的大数据计算框架的同学,推荐学习课程: Flink大数据项目实战:http://t.cn/EJtKhaz ...
- flink学习笔记-数据源(DataSource)
说明:本文为<Flink大数据项目实战>学习笔记,想通过视频系统学习Flink这个最火爆的大数据计算框架的同学,推荐学习课程: Flink大数据项目实战:http://t.cn/EJtKh ...
随机推荐
- 解决方案 | AutoCAD二次开发的ProgID一览表(AutoCAD2004 ~ AutoCAD2024)
1 图片版本 2 文字版本 AutoCAD产品名 版本号 ProgID AutoCAD 2004 R16 AutoCAD.Application.16 AutoCAD 2005 R16.1 AutoC ...
- webgl智慧楼宇发光系列之线性采样下高斯模糊
目录 webgl智慧楼宇发光系列之线性采样下高斯模糊 效率问题 线性采样 代码讲解 总结 参考文档 webgl智慧楼宇发光系列之线性采样下高斯模糊 前面一篇文章 <webgl智慧楼宇发光效果算法 ...
- Java JVM——12. 垃圾回收理论概述
1.前言 1.1 什么是垃圾? 在提到什么是垃圾之前,我们先看下面一张图: 从上图我们可以很明确的知道,Java 和 C++ 语言的区别,就在于垃圾收集技术和内存动态分配上,C++ 语言没有垃圾收集技 ...
- oeasy教您玩转vim - 34 - # 查找进阶
查找进阶 回忆上节课内容 上次是搜索,是全文搜索 和我们以前的行内有点像 / 正向,? 反向 n 保持方向,N 改变方向 hls 让搜索结果高亮 wrapscan 可以从头搜索 noh 取消本次高 ...
- linux date格式化获取时间
转载请注明出处: 在编写shell脚本时,需要在shell脚本中格式化时间,特此整理下date命令相关参数的应用 root@controller1:~# date --help 用法:date [选项 ...
- Day 5 - 双指针与折半搜索
双指针 本页面将简要介绍双指针. 引入 双指针是一种简单而又灵活的技巧和思想,单独使用可以轻松解决一些特定问题,和其他算法结合也能发挥多样的用处. 双指针顾名思义,就是同时使用两个指针,在序列.链表结 ...
- redis如何实现主从同步
redis实现主从同步分为两种:全量同步和增量同步:第一次连入集群的slave需要进行全量同步,那些断开后重连的slave需要进行增量同步 每个redis都有自己的replid,他们是master的标 ...
- 【DataBase】MySQL 26 存储过程
一.概述 存储过程&函数,类似编程语言的方法 什么是方法? 完成特定功能的一组语句 方法的特点 1.可重用性 2.简化操作 二.存储过程[ Stored Procedures]: 一组预先编译 ...
- 《Python数据可视化之matplotlib实践》 源码 第一篇 入门 第一章
最近手上有需要用matplotlib画图的活,在网上淘了本实践书,发现没有代码,于是手敲了一遍,mark下. 第一篇 第一章 图1.1 import matplotlib.pyplot as p ...
- 国产操作系统 “银河麒麟操作系统V10” 试用失败历程
面对外国的科技封锁,具有自主产权的国产软件已经变得迫在眉睫了,几天前在新闻上看到国产的操作"银河麒麟操作系统V10"已经发布,于是抱着尝鲜的心态想着去试着用用.虽然都是基于linu ...