Schedulers(调度器)
用来控制并发并且是中央集权的调度员,允许我们在发生计算时进行协调,例如 setTimeout
或 requestAnimationFrame
或其他。
- 调度器是一种数据结构。 它知道如何根据优先级或其他标准来存储任务和将任务进行排序。
- 调度器是执行上下文。 它表示在何时何地执行任务(举例来说,立即的,或另一种回调函数机制(比如
setTimeout
或process.nextTick
),或动画帧)。 - 调度器有一个(虚拟的)时钟。 调度器功能通过它的
getter
方法now()
提供了“时间”的概念。在具体调度器上安排的任务将严格遵循该时钟所表示的时间。
学到这相信大家也已经或多或少对RxJS
有一定了解了,不知道大家有没有发现一个疑问,前面所展示的代码示例中有同步也有异步,而笔者却没有显示的控制他们的执行,他们的这套执行机制到底是什么呢?
其实他们的内部的调度就是靠的Schedulers
来控制数据发送的时机,许多操作符会预设不同的Scheduler
,所以我们不需要进行特殊处理他们就能良好的进行同步或异步运行。
const source = Rx.Observable.create(function (observer: any) {
observer.next(1);
observer.next(2);
observer.next(3);
observer.complete();
});
console.log('订阅前');
source.observeOn(Rx.Scheduler.async) // 设为 async
.subscribe({
next: (value) => { console.log(value); },
error: (err) => { console.log('Error: ' + err); },
complete: () => { console.log('complete'); }
});
console.log('订阅后');
// 订阅前
// 订阅后
// 1
// 2
// 3
// complete
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
从打印结果上来看,数据的发送时机的确已经由同步变成了异步,如果不进行调度方式修改,那么“订阅后”的打印应该是在数据发送完毕之后才会执行的。
看完示例之后我们再来研究这个调度器能做哪几种调度:
queue
asap
async
animationFrame
# queue
将每个下一个任务放在队列中,而不是立即执行
queue
延迟使用调度程序时,其行为与async
调度程序相同。
当没有延迟使用时,它将同步安排给定的任务-在安排好任务后立即执行。但是,当递归调用时(即在已调度的任务内部),将使用队列调度程序调度另一个任务,而不是立即执行,该任务将被放入队列并等待当前任务完成。
这意味着,当您使用 queue
调度程序执行任务时,您确定它会在该调度程序调度的其他任何任务开始之前结束。
这个同步与我们平常理解的同步可能不太一样,笔者当时也都困惑了一会。
还是用一个官方的例子来讲解这种调度方式是怎么理解吧:
import { queueScheduler } from 'rxjs';
queueScheduler.schedule(() => {
queueScheduler.schedule(() => console.log('second'));
console.log('first');
});
2
3
4
5
6
我们无需关注陌生的函数调用,我们这里着重于看这种调度方式与平常的同步调度的区别。
首先我们调用queueScheduler
的schedule
方法开始执行,然后函数内部又同样再以同样的方式调用(这里也可以改成递归,不过这里用这个示例去理解可能会好一点),并且传入一个函数,打印second
。
然后继续看下面的语句,一个普通的console.log('first')
,然后我们再来看看打印结果:
// first
// second
2
是不是有点神奇,如果没看明白为啥的,可以再回头看看前面queue
对于递归执行的处理方式。也就是说如果递归调用,它内部会维护一个队列,然后等待先加入队列的任务先执行完成(也就是上面的console.log('first')
执行完才会执行console.log('second')
,因为console.log('second')
这个任务是后加入该队列的)。
# asap
内部基于Promise
实现(Node
端采用process.nextTick
),他会使用可用的最快的异步传输机制,如果不支持Promise
或process.nextTick
或者Web Worker
的 MessageChannel
也可能会调用setTimeout
方式进行调度。
# async
与asap
方式很像,只不过内部采用setInterval
进行调度,大多用于基于时间的操作符。
# animationFrame
从名字看其实相信大家已经就能略知一二了,内部基于requestAnimationFrame
来实现调度,所以执行的时机将与window.requestAnimationFrame
保持一致,适用于需要频繁渲染或操作动画的场景。