RxJS 系列 – Scheduler
前言
大部分情况下, RxJS 都是用来处理异步执行的. 比如 Ajax, EventListener 等等.
但其实, 它也是可以同步执行的, 甚至 by default 它就是同步执行的 (下面会给例子).
再加上 JS 的 Event Loop 本来就比较多变化, 所以 RxJS 就有了一个 Scheduler 的概念.
它用来控制执行的时机. 比如我们可以把同步执行, 改成异步执行.
参考
93. RxJS SubscribeOn Operator. Learn RxJS Utility SubscribeOn Operator - RxJS
94. RxJS ObserveOn Operator. Learn RxJS Utility ObserverOn Operator - RxJS
By Default 同步执行
console.log('script start'); // 1
const obs = new Observable(subscriber => {
console.log('Observable start'); // 2
subscriber.next(1); // 3
subscriber.next(2); // 4
console.log('Observable end'); // 5
});
obs.subscribe(v => console.log('subscribe', v)); // 6
console.log('script end'); // 7
结果

by default new Observable 的执行都是同步的.
如果是通过 Creation Operators 或者 Join Creation Operators 来创建 Observable, 那就不一定是同步的.
有一些 operators 创建出来的 Observable default 就是异步的. 比如 timer.
console.log('script start'); // 1
timer(0).subscribe(v => console.log('subscribe', v)); // 3
console.log('script end'); // 2
结果

Types of Scheduler
在讲同步 change to 异步之前, 我们先来看看 RxJS 的有多少种 Scheduler
asyncScheduler
它是 setTimeout 这种 level 的, 异步 Macro
asapScheduler
它是 Promise.resolve 这种 level 的, 异步 Micro
animationFrameScheduler
它是 requestAnimationFrame 这种 level 的
queueScheduler
它是同步的, 但它可不是默认的同步机制哦, 默认的同步 (当我们没有设置时) 的效果和 queueScheduler 是有微小区别的, 下面会详细讲.
同步 change to 异步
subscribeOn
它是一个 pipe operator, 作用是把 subscribe 这个过程变成异步.
console.log('script start');
const obs = new Observable(subscriber => {
console.log('Observable start');
subscriber.next(1);
subscriber.next(2);
console.log('Observable end');
});
obs.pipe(subscribeOn(asyncScheduler)).subscribe(v => console.log('subscribe', v));
console.log('script end');
结果

script end 是同步的, Observable start 开始就是异步了. 类似于做了一个 setTimeout 才去 subscribe.
注: subscribeOn 在 pipe 的任何位置效果都是一样的.
observeOn
observeOn 也是 pipe operator, 它和 subscribeOn 都是把同步转换成异步, 但是结果却有很大区别.
console.log('script start');
const obs = new Observable(subscriber => {
console.log('Observable start');
subscriber.next(1);
subscriber.next(2);
console.log('Observable end');
});
obs
.pipe(
tap(() => console.log('tap 1')),
observeOn(asyncScheduler),
tap(() => console.log('tap 2'))
)
.subscribe(v => console.log('subscribe', v));
console.log('script end');
结果

注意看, script start 到 script end 都是同步的, 只有 tap2 开始才是异步.
这是因为 observeOn 的位置是在 pipe tap 2 之前.
所以 observeOn 不像 subscribeOn 那样把全部都变异步, 它只把后续的 stream 变成异步. 之前的依然是同步.
Creation Operators with Scheduler
上面是通过 pipe operator 把同步变异步. 还有一种方式是从源头开始变异步.
那就是在 creation operator 加上 parameter scheuduler.
from(obs, asyncScheduler)
console.log('script start');
const obs = new Observable(subscriber => {
console.log('Observable start');
subscriber.next(1);
subscriber.next(2);
console.log('Observable end');
});
from(obs, asyncScheduler).subscribe(v => console.log(v));
console.log('script end');
效果

我们看看源码了解一下它干了什么

它底层其实是调用了 scheduled. 注意: from + scheduler 已经快废弃了 (RxJS 8 之后就不能这样用了, 改成用 scheduled)

scheduled 会判断 input 是什么类型, 上面的例子是 observable, 所以进入第一个 if, 执行 scheduleObservable

innerFrom 就是没有 scheduler 的 from (这个可没有废弃哦, 废弃的是 from + scheduler), 然后就是加上我们学过的 subscribeOn 和 observeOn.
以下是 4 种区别

console.log('script start');
const obs = new Observable(subscriber => {
console.log('Observable start');
subscriber.next(1);
subscriber.next(2);
console.log('Observable end');
});
obs.pipe(subscribeOn(asyncScheduler), observeOn(asyncScheduler)).subscribe(v => console.log(v));
console.log('script end');
结果

记得 scheduled(obs, scheduler) 是 subOn + obsOn
scheduled(array, asyncScheduler)
Array 的处理和 Observable 是不同的哦, Observable 只是加上了 pipe subscribeOn 和 observeOn.
Array 我们继续看源码

然后

scheduler.schedule (asyncScheduler)
关键就是这个 scheduler.schedule
上面我们讲了 asyncScheduler 相等于 setTimeout level 的异步(Macro), asapScheduler 相等于 Promose.resolve level 的异步(Micro)
它们底层也确实就是用 setInterval 和 Promise.resolve 来完成的.

asyncScheduler 就是 AsyncScheduler + AsyncAction. 这两个类内部会互相调用对方的方法. 挺乱的.

所有 Scheduler 都继承了 Scheduler class 只是 override 了 Action 和 flush 的逻辑
asyncScheduler.schedule 的接口是

它创建 Action 实例, 然后把 callback work 丢进去, 并调用 Action 的 schedule

关键就是这个 setInterval 了. 之后的 flush 就是运行我们传入的 callback 等等. 我们点到为止就好了.
所以呢,
console.log('script start');
asyncScheduler.schedule(() => console.log('call'));
console.log('script end');
结果是

因为里面有一个 setInterval
scheduler.schedule (asapScheduler)
再看一个 AsapAction 例子

它里面就是一个 Promise.resolve

schedule(array, scheduler) vs subscribeOn vs observeOn

在 for loop 的时候, 它是把每一个值都做了异步处理.
所以
scheduled([1,2,3], asyncScheduler).subscribe()
from([1,2,3]).pipe(subscribeOn(asyncScheduler), observeOn(asyncScheduler)).subscribe()
这 2 个操作的结果是不一样的.
scheduled 的 1, 2, 3 每一次 next value 都会进入一个 event loop 都是异步.
而 subscribeOn 只是在延迟了 subscribe, 之后的 next 依然是同步的
而 observeOn 是把前面的流延迟发布下去. 但是源头依然是同步的.
只有 scheduled 是把源头的 1, 2, 3 发布变成了每一次都是延迟.
queueScheduler vs no scheduler

首先 sourceA$ 直接发布 1, 2. 而 sourceB$ 是空的. combineLatest 不会发布
然后 sourceB$ 发布 3, 这时 combineLatest 发布 [2, 3] = 5
然后 sourceB$ 发布 4, 这时 combineLatest 发布 [2, 4] = 6
目前的发布顺序是 a1, a2, b1, b2
有没有可能让它变成 a1, b1, a2, b2 呢? 有, 用 queueScheduler

全部依然是同步的, 只是发布顺序变成了 a1, b1, a2, b2
于是
首先 sourceA$ 发布 1, 而 sourceB$ 是空的. combineLatest 不会发布
然后 sourceB$ 发布 3, 这时 combineLatest 发布 [1, 3] = 4
然后 sourceA$ 发布 2, 这时 combineLatest 发布 [2, 3] = 5
然后 sourceB$ 发布 4, 这时 combineLatest 发布 [2, 4] = 6
总结
1. RxJS by default 是同步的
2. 许多 operator 有自己的 scheduler 比如, timer 是 asyncScheduler.
3. asyncScheduler = setInterval level (异步 Macro),
asapScheduler = Promise.resolve (异步 Micro),
animationFrameScheduler = requestAnimationFrame (异步 Macro),
queueScheduler 是同步但和默认同步有确保
4. pipe operator subscribeOn 是 delay subscribe, 但没有 delay 后续的发布 (放在 pipe 任何位置效果一样)
5. pipe operator observeOn 是 delay 后续的发布, 但是没有 delay 源头 (放在 pipe 的位置不同效果不同)
6. schedule([1,2,3], asyncScheduler) 从源头开始 delay 发布, 效果 1 -> delay -> 2 -> delay -> 3
7. queueScheduler 能修改同步的发布顺序. 通常用在 combineLatest
scheduler 有一点点复杂, 很多时候你不会看出它有啥用. 但是不要紧, 学起来有个印象就好, 需要的时候你自然会用上它.
RxJS 系列 – Scheduler的更多相关文章
- RxJS——调度器(Scheduler)
调度器 什么是调度器?调度器是当开始订阅时,控制通知推送的.它由三个部分组成. 调度是数据结构.它知道怎样在优先级或其他标准去存储和排队运行的任务 调度器是一个执行上下文.它表示任务在何时何地执行(例 ...
- 第3章 从Flux到Redux
第3章 从Flux到Redux 3.1 Flux 单向数据流,React是用来替换Jquery的,Flux是以替换Backbone.js.Ember.js等MVC框架为主的. actionTypes. ...
- AndroidStudio3.0无法打开Android Device Monitor的解决办法(An error has occurred on Android Device Monitor)
---恢复内容开始--- 打开monitor时出现 An error has occurred. See the log file... ------------------------------- ...
- [Redux-Observable && Unit Testing] Use tests to verify updates to the Redux store (rxjs scheduler)
In certain situations, you care more about the final state of the redux store than you do about the ...
- 图解 kubernetes scheduler 架构设计系列-初步了解
资源调度基础 scheudler是kubernetes中的核心组件,负责为用户声明的pod资源选择合适的node,同时保证集群资源的最大化利用,这里先介绍下资源调度系统设计里面的一些基础概念 基础任务 ...
- QUARTZ系列之一-基础概念(Scheduler/Job/JobDetail/Trigger)
摘抄自quartz官方文档: The key interfaces of the Quartz API are: Scheduler - the main API for interacting wi ...
- Quartz.Net系列(四):Quartz五大构件(Scheduler,Job,Trigger,ThreadPool、JobStore)之ThreadPool、JobStore解析
整体示意图: 1.DefaultThreadPool 如果不存在PropertyThreadPoolType,那么就使用DefaultThreadPool var threadPoolTypeStri ...
- Angular2入门系列教程7-HTTP(一)-使用Angular2自带的http进行网络请求
上一篇:Angular2入门系列教程6-路由(二)-使用多层级路由并在在路由中传递复杂参数 感觉这篇不是很好写,因为涉及到网络请求,如果采用真实的网络请求,这个例子大家拿到手估计还要自己写一个web ...
- Angular2入门系列教程6-路由(二)-使用多层级路由并在在路由中传递复杂参数
上一篇:Angular2入门系列教程5-路由(一)-使用简单的路由并在在路由中传递参数 之前介绍了简单的路由以及传参,这篇文章我们将要学习复杂一些的路由以及传递其他附加参数.一个好的路由系统可以使我们 ...
- Angular2入门系列教程5-路由(一)-使用简单的路由并在在路由中传递参数
上一篇:Angular2入门系列教程-服务 上一篇文章我们将Angular2的数据服务分离出来,学习了Angular2的依赖注入,这篇文章我们将要学习Angualr2的路由 为了编写样式方便,我们这篇 ...
随机推荐
- 全网最适合入门的面向对象编程教程:08 类和对象的Python实现-@property装饰器:把方法包装成属性
全网最适合入门的面向对象编程教程:08 类和对象的 Python 实现-@property 装饰器:把方法包装成属性 摘要: 本文主要对@property 装饰器的基本定义.使用场景和使用方法进行了介 ...
- CPU的保护模式
保护模式是为了克服实模式低劣的内存管理方式,物理内存地址不能直接被程序访问,程序内部的地址需要被转化为物理地址后再去访问.实模式CPU运行环境16位,保护模式32位. 寄存器扩展: 由于CPU发展到3 ...
- [oeasy]python0071_字符串类型_str_string_下标运算符_中括号
回忆上次内容 上次 分辨了 静态类型 语言 动态类型 语言 python 属于 对类型要求 没有那么严格的 动态类型 语言 对 初学者很友好 不过很多时候 也容易 弄不清变量类型 直接 修 ...
- MySQL之DML
DQL:SELECT * FROM 表名 DML(数据操作语言,它是对表记录的操作(增.删.改)!) 1. 插入数据 * INSERT INTO 表名(列名1,列名2, ...) VALUES(列值1 ...
- Odoo 基于Win10搭建基于Win10搭建odoo14开发环境搭建
实践环境 win10 Python 3.6.2 odoo_14.0.latest.tar.gz 下载地址: https://download.odoocdn.com/download/14/src?p ...
- Week 0
Day 0 我8:00与lsh前往济南,在大约11:40左右抵达济南高铁站. 等高铁期间,我在庞大的济南高铁站中找到了我心心念念的而DY却没有的赛百味三明治,买了27的西式火腿三明治和34的香烤牛肉三 ...
- Cython与C函数的结合
技术背景 在前面一篇博客中,我们介绍了使用Cython加速谐振势计算的方法.有了Cython对于计算过程更加灵活的配置(本质上是时间占用和空间占用的一种均衡),及其接近于C的性能,并且还最大程度上的保 ...
- mybatis关于大于小于:元素内容必须由格式正确的字符数据或标记组成。
首先是原因: mybatis中< >这两个符号会被识别为标签的开始和结束,用了就会报解析的错误 会报错类似下面这些 1.元素内容必须由格式正确的字符数据或标记组成. 2.Error cre ...
- 初学者使用1Panel面板快速搭建WordPress网站
之前介绍了宝塔面板以及如何搭建wordpress网站,这篇文章我们来学习如何使用1Panel面板搭建wordpress网站. 一.1Panel面板介绍 1. 介绍 1Panel 是一个现代化.开源的基 ...
- 1、Git简介
1.1.概述 Git 是一个开源免费的分布式版本控制系统,用于快速高效地管理各种小型或大型项目的代码. Git 不仅容易学习.占用空间小,而且性能快如闪电. Git 具有廉价的本地分支.方便的暂存区域 ...