「这是我参与11月更文挑战的第21天,活动详情查看:2021最后一次更文挑战」
上一篇文章提到了 react 有 cancelCallback、scheduleCallback、scheduleSyncCallback、scheduleMicrotask 等函数,其中的 cancelCallback、scheduleCallback都是来自于一个叫scheduler 的库,今天我们一起来看看 scheduler 做了些什么?
// packages/react-reconciler/src/Scheduler.js
import * as Scheduler from 'scheduler';
export const scheduleCallback = Scheduler.unstable_scheduleCallback;
export const cancelCallback = Scheduler.unstable_cancelCallback;
scheduler 介绍
一句话来说就是 React 是用它来在浏览器环境进行协同调度的。 一开始我以为这是一个第三方库,后来发现它就在 react 仓库的 packages/scheduler 目录下。
它的index.js就一行 export 语句:export * from './src/forks/Scheduler'; 按图索骥来到Scheduler.js,约600行的代码,但先不从头开始看,先来看我关心的 unstable_scheduleCallback 和 unstable_cancelCallback
unstable_scheduleCallback
函数签名
function unstable_scheduleCallback(priorityLevel, callback, options) {}
代码逻辑
- 获取当前时间
currentTime,加上延迟时间options.delay,计算出开始时间startTime - 根据
priorityLevel的取值得到不同的timeout - 过期时间 expirationTime = startTime + timeout
- 创建一个新的任务
var newTask = {
id: taskIdCounter++,
callback,
priorityLevel,
startTime,
expirationTime,
sortIndex: -1,
};
- 如果
newTask是延迟任务,即延迟时间不为 0,以startTime为排序索引(sortIndex),放入名为timerQueue的优先级队列中。 - 如果
taskQueue为空,该任务是timerQueue中优先级最高的,检查isHostTimeoutScheduled标志,如果为true, 则调用cancelHostTimeout函数,否则标记isHostTimeoutScheduled为 true。检查完isHostTimeoutScheduled之后,调度一个 timeout。(本质上就是如果新任务优先级最高,则取消原本将要执行的任务,优先执行新任务)
requestHostTimeout(handleTimeout, startTime - currentTime);
- 如果
newTask不是延迟任务,则以过期时间为排序索引,放入任务队列taskQueue。 - 如果
isHostCallbackScheduled为 false 且isPerformingWork为 false,标记isHostCallbackScheduled为 true,执行requestHostCallback(flushWork); - 最后返回 newTask
过完上面的代码逻辑,会产生新的疑问—— requestHostTimeout 和 cancelHostTimeout 分别做了什么?requestHostCallback 又做了什么?
requestHostTimeout 本质就是一个 setTimeout 定时器类型的宏任务调度。
function requestHostTimeout(callback, ms) {
taskTimeoutID = localSetTimeout(() => {
callback(getCurrentTime());
}, ms);
}
这里的 localSetTimeout 就是 setTimeout 函数的别名,取别名是为了防止用户在后面重写了全局的 setTimeout 函数导致功能失效。
同理,localClearTimeout 是 clearTimeout 的别名。cancelHostTimeout 的作用就是调用了 localClearTimeout 并将 taskTimeoutID 重置为 -1.
至于 requestHostCallback,它会接受一个 callback 参数,赋值给全局变量 scheduledHostCallback,然后开启 callbackschedulePerformWorkUntilDeadline -> performWorkUntilDeadline -> scheduledHostCallback ->schedulePerformWorkUntilDeadline 消息大循环,直到 scheduledHostCallback 调用的返回值为 false,将scheduledHostCallback 重置为 null。
前面我们看到被传入 `requestHostCallback 的是 flushWork,后者的核心代码如下:
function flushWork(hasTimeRemaining, initialTime) {
isHostCallbackScheduled = false;
if (isHostTimeoutScheduled) {
// 我们已经调度了一个 timeout,但是它已经没用了,退出该 timeout
isHostTimeoutScheduled = false;
cancelHostTimeout();
}
isPerformingWork = true;
const previousPriorityLevel = currentPriorityLevel;
try {
return workLoop(hasTimeRemaining, initialTime);
} finally {
currentTask = null;
currentPriorityLevel = previousPriorityLevel;
isPerformingWork = false;
}
}
unstable_cancelCallback
内容很简单,就是将入参的 callback字段置空
function unstable_cancelCallback(task) {
if (enableProfiling) {
// ...
}
// Null out the callback to indicate the task has been canceled. (Can't
// remove from the queue because you can't remove arbitrary nodes from an
// array based heap, only the first one.)
task.callback = null;
}
下期预告
- flushWork 中的 workLoop 做了什么?
- 这个库实现任务调度的整体原理是什么?