基本概念

Schedulers 是用来控制并发并且是中央集权的调度员,允许我们在发生计算时进行协调,例如 setTimeoutrequestAnimationFrame 或其他。

  • 调度器是一种数据结构。它知道如何根据优先级或其他标准来存储任务和将任务进行排序。
  • 调度器是执行上下文。它表示在何时何地执行任务 (举例来说,立即的,或另一种回调函数机制 (比如 setTimeoutprocess.nextTick),或动画帧)。
  • 调度器有一个 (虚拟的) 时钟。调度器功能通过它的 getter 方法 now() 提供了 “时间” 的概念。在具体调度器上安排的任务将严格遵循该时钟所表示的时间。

学到这相信大家也已经或多或少对 RxJS 有一定了解了,不知道大家有没有发现一个疑问,前面所展示的代码示例中有同步也有异步,而笔者却没有显示的控制他们的执行,他们的这套执行机制到底是什么呢?
其实他们的内部的调度就是靠的 Schedulers 来控制数据发送的时机,许多操作符会预设不同的 Scheduler,所以我们不需要进行特殊处理他们就能良好的进行同步或异步运行。

  1. const source = Rx.Observable.create(function (observer: any) {
  2. observer.next(1);
  3. observer.next(2);
  4. observer.next(3);
  5. observer.complete();});
  6. console.log('订阅前');
  7. source.observeOn(Rx.Scheduler.async) // 设为 async
  8. subscribe({
  9. next: (value) => { console.log(value); },
  10. error: (err) => { console.log('Error: ' + err); },
  11. complete: () => { console.log('complete'); }
  12. });
  13. console.log('订阅后');
  14. // 订阅前
  15. // 订阅后
  16. // 1
  17. // 2
  18. // 3
  19. // complete

从打印结果上来看,数据的发送时机的确已经由同步变成了异步,如果不进行调度方式修改,那么 “订阅后” 的打印应该是在数据发送完毕之后才会执行的。

调度方法

看完示例之后我们再来研究这个调度器能做哪几种调度:

  • queue
  • asap
  • async
  • animationFrame

    queue

    将每个下一个任务放在队列中,而不是立即执行

    queue 延迟使用调度程序时,其行为与 async 调度程序相同。

当没有延迟使用时,它将同步安排给定的任务 - 在安排好任务后立即执行。但是,当递归调用时(即在已调度的任务内部),将使用队列调度程序调度另一个任务,而不是立即执行,该任务将被放入队列并等待当前任务完成。
这意味着,当您使用 queue 调度程序执行任务时,您确定它会在该调度程序调度的其他任何任务开始之前结束。

这个同步与我们平常理解的同步可能不太一样,笔者当时也都困惑了一会。

还是用一个官方的例子来讲解这种调度方式是怎么理解吧:

import { queueScheduler } from 'rxjs';

queueScheduler.schedule(() => { 
  queueScheduler.schedule(() => console.log('second')); 
  console.log('first');
});

我们无需关注陌生的函数调用,我们这里着重于看这种调度方式与平常的同步调度的区别。

首先我们调用 queueScheduler 的 schedule 方法开始执行,然后函数内部又同样再以同样的方式调用(这里也可以改成递归,不过这里用这个示例去理解可能会好一点),并且传入一个函数,打印 second。
然后继续看下面的语句,一个普通的 console.log('first'),然后我们再来看看打印结果:

// first
// second

是不是有点神奇,如果没看明白为啥的,可以再回头看看前面 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 保持一致,适用于需要频繁渲染或操作动画的场景。