携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第11天,点击查看活动详情
scheduler
Scheduler在日常工作中使用还是很多的,主要用它来处理一些异步操作。
Rxjs中一共内置了4种调度器,AsyncScheduler,QueueScheduler,AsapScheduler,和AnimationFrameScheduler,其他三个调度器都继承自AsyncScheduler,所以,我们先把AsyncScheduler搞清楚。
asyncScheduler
Async Scheduler
先来看一下英文解释:
Schedule task as if you used setTimeout(task, duration).
async
scheduler schedules tasks asynchronously, by putting them on the JavaScript event loop queue. It is best used to delay tasks in time or to schedule tasks repeating in intervals.
If you just want to "defer" task, that is to perform it right after currently executing synchronous code ends (commonly achieved by setTimeout(deferredTask, 0)
), better choice will be the {@link asapScheduler} scheduler.
import { asyncScheduler } from 'rxjs';
const task = () => console.log('it works!');
asyncScheduler.schedule(task, 2000);
// After 2 seconds logs:
// "it works!"
// 就像setTimeout一样
export const asyncScheduler = new AsyncScheduler(AsyncAction);
Rxjs中export的只有asyncScheduler,并没有AsyncScheduler。构造函数有一个参数AsyncAction。它继承自Scheduler。
export class AsyncScheduler extends Scheduler {
public actions: Array<AsyncAction<any>> = [];
public _active: boolean = false;
public _scheduled: any = undefined;
constructor(SchedulerAction: typeof Action, now: () => number = Scheduler.now) {
super(SchedulerAction, now);
}
public flush(action: AsyncAction<any>): void {
...
}
}
Scheduler里有一个schedule方法。
export class Scheduler implements SchedulerLike {
public static now: () => number = dateTimestampProvider.now;
constructor(private schedulerActionCtor: typeof Action, now: () => number = Scheduler.now) {
this.now = now;
}
public now: () => number;
public schedule<T>(work: (this: SchedulerAction<T>, state?: T) => void, delay: number = 0, state?: T): Subscription {
return new this.schedulerActionCtor<T>(this, work).schedule(state, delay);
}
}
那这里的异步,到底是用什么来实现的呢?setTimeout,还是setInterval?其实单纯从功能上来看,这两个都是可以的,那用哪个会更好一点呢?
Important implementation note:
Actions only execute once by default, unless rescheduled from within the
scheduled callback. This allows us to implement single and repeat
actions via the same code path, without adding API surface area, as well
as mimic traditional recursion but across asynchronous boundaries.
However, JS runtimes and timers distinguish between intervals achieved by
serial 'setTimeout' calls vs. a single 'setInterval' call. An interval of
serial 'setTimeout' calls can be individually delayed, which delays
scheduling the next 'setTimeout', and so on. 'setInterval' attempts to
guarantee the interval callback will be invoked more precisely to the
interval period, regardless of load.
Therefore, we use 'setInterval' to schedule single and repeat actions.
If the action reschedules itself with the same delay, the interval is not
canceled. If the action doesn't reschedule, or reschedules with a
different delay, the interval will be canceled after scheduled callback
execution.
大致意思是说,setTimeout可以将task单独的进行延迟,而setInterval可以保证每个task的延迟更加的精确,也就是它不受上一个task的影响。如果setTimeout的第一个任务执行时间比较长,比如执行了5秒,然后才开始执行下一个task,这就导致了下一个task被延迟的时间受到了影响。
因此,采用setInterval来调度一个单一的task,或重复的task。就是用setInterval来实现setTimeout和setInterval的功能。
好了,开始撸它。
class Schedule {
schedule(task, period){
let id = setInterval(() => {
task && task();
if (this._action && this._action === 'single') {
clearInterval(id);
}
}, period ? period : 0);
}
}
// action: single/repeat
class AsyncScheduler extends Schedule {
constructor(action){
super();
this._action = action;
}
}
const asyncScheduler = new AsyncScheduler('single');
这块简化了很多代码,方便理解,不去过度的封装增加理解的难度。
asapScheduler
asapScheduler是尽可能快的以异步的方式执行任务。既然是异步任务,那先来看一下JS中有哪些异步的API吧。
- setTimeout,setInterval,setImmediate,XHR,DOM事件,postMessage,MessageChannel,I/O(NodeJS);
- Promise,MutationObserver,process.nextTick(NodeJS);
第一个行属于宏任务,第二行属于微任务,微任务的执行时机先于宏任务,所以应该选择微任务。MutationObserver和DOM相关,nextTick属于NodeJS,这两个都不合适,那就只有Promise了。
其他三个调度器QueueScheduler,AsapScheduler,和AnimationFrameScheduler都继承自AsyncSchedule,我们实现了AsyncSchedule,其实离其他三个调度器就不远了,这只是走了第一步,后续再来实现他们吧,这个专栏暂时不实现了。