前言

大部分情况下, 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. 国内中文版的 Chātgpt,真的好赞。

    这个软件是「Chātgpt」,Chātgpt 中文版是国内刚上线的一款人工智能技术的语言处理工具软件,它不仅可以学习和理解人类的语言来和人对话,还能撰写邮件.文案.翻译.代码等功能. 不需要OpenA ...

  2. 解决方案 | onenote无法同步,显示:证书错误,应用程序在加载SSL库是遇到内部错误。

    解决方案:一般是公司网络或者学校网络的问题,更换手机使用的数据流量热点无线网络即可.

  3. thinkphp模型hasOne、hasMany、belongsTo详解

    在ThinkPHP框架中,hasOne.hasMany和belongsTo是用于定义模型间一对多(1:n).一对一(1:1)和多对一(n:1)关联关系的方法.以下是一些简单的示例来解释这些关系: 1. ...

  4. elementplus django drf 如何做到确认单据禁止删除

    elementplus django drf 如何做到确认单据禁止删除     要在Django和Django Rest Framework(DRF)中实现禁止删除确认单据的功能,你可以通过以下步骤来 ...

  5. oeasy 教您玩转linux 之010208 满屏乱码 bb

    我们来回顾一下 上一部分我们都讲了什么? 黑客帝国 各种速度颜色参数 最后我们想找一个让人完全崩溃的软件包,这不就来了,话不多说,先试试. apt show bbapt search bbsudo a ...

  6. 文件系统(十):一文看懂 UBI 文件系统

    liwen01 2024.07.21 前言 UBI (Unsorted Block Images)文件系统是一种用于裸 flash 的文件系统管理层.它是专为管理原始闪存设备而设计,特别适用于嵌入式系 ...

  7. iOS开发基础142-广告归因

    IDFA IDFA是苹果为iOS设备提供的一个唯一标识符,专门用于广告跟踪和相关的营销用途.与之对应的,在Android平台的是谷歌广告ID(Google Advertising ID). IDFA的 ...

  8. TCP和KCP协议

    TCP协议 KCP是一个快速可靠协议,能以比 TCP 浪费 10%-20% 的带宽的代价,换取平均延迟降低 30%-40%,且最大延迟降低三倍的传输效果.纯算法实现,并不负责底层协议(如UDP)的收发 ...

  9. DP进阶合集

    (ps:本集合为Star_F总结的dp进阶知识,持续更新~. 转载本文章需要联系我,否则视为侵权!!) 前置知识:线性dp,背包,树形dp,区间dp 内容预览: 状压dp 数位dp dp优化(前缀和, ...

  10. 【Excel】VBA编程 01 入门

    视频地址: https://www.bilibili.com/video/BV1Q5411p71p 在Excel种需要打开[开发工具]和[启用所有宏]两点 打开开发工具选项 宏启用 菜单栏才会有开发工 ...