前言

大部分情况下, RxJS 都是用来处理异步执行的. 比如 Ajax, EventListener 等等.

但其实, 它也是可以同步执行的, 甚至 by default 它就是同步执行的 (下面会给例子).

再加上 JS 的 Event Loop 本来就比较多变化, 所以 RxJS 就有了一个 Scheduler 的概念.

它用来控制执行的时机. 比如我们可以把同步执行, 改成异步执行.

参考

認識 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的更多相关文章

  1. RxJS——调度器(Scheduler)

    调度器 什么是调度器?调度器是当开始订阅时,控制通知推送的.它由三个部分组成. 调度是数据结构.它知道怎样在优先级或其他标准去存储和排队运行的任务 调度器是一个执行上下文.它表示任务在何时何地执行(例 ...

  2. 第3章 从Flux到Redux

    第3章 从Flux到Redux 3.1 Flux 单向数据流,React是用来替换Jquery的,Flux是以替换Backbone.js.Ember.js等MVC框架为主的. actionTypes. ...

  3. AndroidStudio3.0无法打开Android Device Monitor的解决办法(An error has occurred on Android Device Monitor)

    ---恢复内容开始--- 打开monitor时出现 An error has occurred. See the log file... ------------------------------- ...

  4. [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 ...

  5. 图解 kubernetes scheduler 架构设计系列-初步了解

    资源调度基础 scheudler是kubernetes中的核心组件,负责为用户声明的pod资源选择合适的node,同时保证集群资源的最大化利用,这里先介绍下资源调度系统设计里面的一些基础概念 基础任务 ...

  6. QUARTZ系列之一-基础概念(Scheduler/Job/JobDetail/Trigger)

    摘抄自quartz官方文档: The key interfaces of the Quartz API are: Scheduler - the main API for interacting wi ...

  7. Quartz.Net系列(四):Quartz五大构件(Scheduler,Job,Trigger,ThreadPool、JobStore)之ThreadPool、JobStore解析

    整体示意图: 1.DefaultThreadPool 如果不存在PropertyThreadPoolType,那么就使用DefaultThreadPool var threadPoolTypeStri ...

  8. Angular2入门系列教程7-HTTP(一)-使用Angular2自带的http进行网络请求

    上一篇:Angular2入门系列教程6-路由(二)-使用多层级路由并在在路由中传递复杂参数 感觉这篇不是很好写,因为涉及到网络请求,如果采用真实的网络请求,这个例子大家拿到手估计还要自己写一个web ...

  9. Angular2入门系列教程6-路由(二)-使用多层级路由并在在路由中传递复杂参数

    上一篇:Angular2入门系列教程5-路由(一)-使用简单的路由并在在路由中传递参数 之前介绍了简单的路由以及传参,这篇文章我们将要学习复杂一些的路由以及传递其他附加参数.一个好的路由系统可以使我们 ...

  10. Angular2入门系列教程5-路由(一)-使用简单的路由并在在路由中传递参数

    上一篇:Angular2入门系列教程-服务 上一篇文章我们将Angular2的数据服务分离出来,学习了Angular2的依赖注入,这篇文章我们将要学习Angualr2的路由 为了编写样式方便,我们这篇 ...

随机推荐

  1. Jenkins插件管理(Manager Plugins)【快速提升项目构建和部署实施的工作效率】

    Jenkins 是一个很棒的开源自动化平台.它有一些开箱即用的强大功能.然而,在我看来,让它脱颖而出的是它的社区和它开发的插件.有超过一千个插件可用于支持几乎所有用于构建.部署和自动化项目的技术.工具 ...

  2. [BJDCTF2020]Mark loves cat(源码泄露+命令执行)

    扫描之后发现是/.git源码泄露 python GitHack.py http://56ad87c1-d8fb-463d-9480-f0fbee5176a0.node5.buuoj.cn:81/.gi ...

  3. 图解翻转单向链表,超详细(python语言实现)

    节点类: 1 class ListNode(object): 2 def __init__(self, x): 3 self.val = x 4 slef.next = None 反转单向链表的函数如 ...

  4. 提高MQ可靠性

    提高可靠性通过以下四个方面: 生产者的可靠性(发送消息时丢失) 生产者发送消息时连接MQ失败 生产者发送消息到达MQ后未找到exchange 生产者发生消息到达MQ的exchange后,未找到合适的q ...

  5. python解决urllib发送请求报错:urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:xxxx)>

    在使用urllib.request.Request(url)前,添加代码放到最前面 import ssl ssl._create_default_https_context = ssl._create ...

  6. Codeforces Round 953 (Div. 2)

    Codeforces Round 953 (Div. 2) 闲来无事水题解. A . B . C 显然 \(k\) 是偶数.考虑 \(k\) 的上界,\(p_{1}=n,p_{n}=1\),产生 \( ...

  7. python global将结果存储起来给另外一个文件对象使用

    python global将结果存储起来给另外一个文件对象使用 使用场景: 在aaa.py文件里面操作数据生成结果C 然后再在bbb.py文件里面使用C 下面是aaa.py代码: #!/usr/bin ...

  8. 【MySQL】01 概念与介绍

    视频节选自 :P1 - P7 https://www.bilibili.com/video/BV1xW411u7ax  用户浏览的页面 - 服务器 - 数据库 所有访问的本质的东西,就是访问数据,数据 ...

  9. 用一杯星巴克的钱,训练自己私有化的ChatGPT

    文章摘要:用一杯星巴克的钱,自己动手2小时的时间,就可以拥有自己训练的开源大模型,并可以根据不同的训练数据方向加强各种不同的技能,医疗.编程.炒股.恋爱,让你的大模型更"懂"你-. ...

  10. Gradle 项目打开自动下载Zip问题及相关配置

    原因 : 由于使用Eclipse开发,导入了SpringCloud 工程,SpringCloud 自从哪个版本忘了昂,选择了Gradle 作为工程管理工具,至于为啥,你去问问官方,我的了解是为了支持G ...