# Scheduler调度延时任务 ## *unstable_scheduleCallback* ```js function unstable_scheduleCallback(priorityLevel,callback,options){ //... if (startTime > currentTime) { // 调度一个延时任务 requestHostTimeout(handleTimeout, startTime - currentTime); } else { // 调度一个普通任务 requestHostCallback(flushWork); } } ``` - 可以看到,调度一个延时任务的时候,主要是执行 requestHostTimeout ## requestHostTimeout ```js // 实际上在浏览器环境就是 setTimeout const localSetTimeout = typeof setTimeout === 'function' ? setTimeout : null; /** * * @param {*} callback 就是传入的 handleTimeout * @param {*} ms 延时的时间 */ function requestHostTimeout(callback, ms) { taskTimeoutID = localSetTimeout(() => { callback(getCurrentTime()); }, ms); /** * 因此,上面的代码,就可以看作是 * id = setTimeout(function(){ * handleTimeout(getCurrentTime()) * }, ms) */ } ``` 可以看到,requestHostTimeout 实际上就是调用 setTimoutout,然后在 setTimeout 中,调用传入的 handleTimeout ## handleTimeout ```js /** * * @param {*} currentTime 当前时间 */ function handleTimeout(currentTime) { isHostTimeoutScheduled = false; // 遍历timerQueue,将时间已经到了的延时任务放入到 taskQueue advanceTimers(currentTime); if (!isHostCallbackScheduled) { if (peek(taskQueue) !== null) { // 从普通任务队列中拿一个任务出来 isHostCallbackScheduled = true; // 采用调度普通任务的方式进行调度 requestHostCallback(flushWork); } else { // taskQueue任务队列里面是空的 // 再从 timerQueue 队列取一个任务出来 // peek 是小顶堆中提供的方法 const firstTimer = peek(timerQueue); if (firstTimer !== null) { // 取出来了,接下来取出的延时任务仍然使用 requestHostTimeout 进行调度 requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime); } } } } ``` - handleTimeout 里面主要就是调用 advanceTimers 方法,该方法的作用是将时间已经到了的延时任务放入到 taskQueue,那么现在 taskQueue 里面就有要执行的任务,然后使用 requestHostCallback 进行调度。如果 taskQueue 里面没有任务了,再次从 timerQueue 里面去获取延时任务,然后通过 requestHostTimeout 进行调度。 ## 流程图 Scheduler 这一块儿大致的流程图如下: image-20221230103504711