大家好,我是Kason。调度器(scheduler)是React的重要组成部分。同时,它也是一个独立的包。任何连续和可中断的进程都可以被调度器调度,例如:constwork={count:100};functiondoWork(work){work.count--;console.log('dowork!')}work满足两个条件:工作是连续的。一共需要执行100次,每次执行doWork的调用都是可以中断的。中断恢复后,中断前的work.count会继续执行,只要满足这两个条件的工作就可以被Scheduler调度。调度完成后,Scheduler会在内部生成相应的任务,并在合适的时候执行task.callback:consttask1={//过期时间等于当前时间+优先级对应时间expirationTime:currentTime+priority,callback:doWork.bind(null,work)}本文将讲解Scheduler的实现原理。我知道你不喜欢阅读大段代码,所以本文没有一行代码。文末有Scheduler的源码地址。如果您有兴趣,可以查看一下。开征~工作流程概述Scheduler的工作原理如下图所示,接下来会详细解释:Scheduler中有两个容易混淆的概念:delaydelay表示task需要延迟执行的时间。配置了delay的任务会先进入timerQueue。当延迟对应的时间到期时,任务会被转移到taskQueue中。expirationTimeexpirationTime表示任务的过期时间。并不是所有的任务都会配置延时,没有延时的任务会直接进入taskQueue。这就导致了taskQueue中有多个任务的可能性。如何决定先执行哪个task.callback?Scheduler根据task.expirationTime排序,值越小优先级越高。如果task.expirationTime小于当前时间,不仅优先级最高,而且task.callback的执行也不会被打断。总结一下task的几种情况:配置了延时且延时未过期:任务一定没有执行配置的延时和过期,或者没有配置延时的任务,task.expirationTime还未过期:按照task排序后.expirationTime,pressSequentialexecutionoftaskswithtask.expirationTimeexpired:最高优先级,同步,不可中断的工作流程详解将流程概览图替换成Scheduler中的具体方法后如下:完整的工作流程如下:执行Scheduler.scheduleCallback根据是否下达delay参数生成任务,生成的任务会进入timerQueue或taskQueue。当timerQueue中第一个任务的延迟时间到期时,执行advanceTimers将超时的任务从timerQueue移到taskQueue中。timerQueue和taskQueue的数据结构都是小顶堆实现的优先级队列。接下来执行requestHostCallback方法,会在新的宏任务中执行workLoop方法。在宏任务中执行回调的方式有很多种。Scheduler在浏览器环境下默认使用MessageChannel实现。如果不支持MessageChannel,则会降级为setTimeout。Node或旧版本的IE将使用setImmediate。workLoop方法会循环消费taskQueue中的任务(即执行task.callback),直到满足以下条件之一,中断循环:taskQueue中没有任务。时间片用完了。循环中断后,如果taskQueue不为空,转步骤3。如果timerQueue不为空,则转步骤2。综上所述,Scheduler完整的执行过程包括两个循环:taskQueue的生产(从timerQueue移入或执行scheduleCallback的生成)到消费的过程(即灰度)图中的部分),这是一个异步循环taskQueue的具体消费过程(也就是workLoop方法的执行)。这是一个同步循环。如果想了解如何在React中使用Scheduler,可以参考100行代码实现React的核心调度功能。欢迎加入人类优质前端框架群,一起飞翔
