React Scheduler
1. unstable_scheduleCallback
react调度过程的入口是unstable_scheduleCallback
函数,该函数接收调度任务的优先级priorityLevel、任务函数callback以及option三个参数。priorityLevel用来计算任务的timeout时间,对应关系如下:
ImmediatePriority => IMMEDIATE_PRIORITY_TIMEOUT : -1
UserBlockingPriority => USER_BLOCKING_PRIORITY : 250
IdlePriority => IDLE_PRIORITY : 5000
LowPriority => LOW_PRIORITY_TIMEOUT : 10000
NormalPriority => NORMAL_PRIORITY_TIMEOUT : maxSigned31BitInt
option
参数包含delay和timeout选项,分别决定任务的startTime和timout,这两者相加得到任务的过期时间(expirationTime)。unstable_scheduleCallback
中新建的newTask结构如下:
var newTask = {
id: taskIdCounter++,
callback,
priorityLevel,
startTime,
expirationTime,
sortIndex: -1,
};
接下来将newTask按startTime的区别推入最小堆timeQueue或者taskQueue,开始调度过程(最小堆结构可以方便的从中取出优先级最高的任务)。
2. delayed task的处理
当task的startTime < currentTime
时,说明这是一个延时任务,将该任务推入timeQueue,timeQueue是按startTime排列的最小堆结构。推入任务后,如果taskQueue为空且这个新推入的延时任务是timeQueue中的第一个任务,则到该任务的startTime时,调用handleTimeout(currentTime)
将timeQueue中startTime >= currentTime
的task推入taskQueue。
if (startTime > currentTime) {
// This is a delayed task.
newTask.sortIndex = startTime;
push(timerQueue, newTask);
if (peek(taskQueue) === null && newTask === peek(timerQueue)) {
// All tasks are delayed, and this is the task with the earliest delay.
if (isHostTimeoutScheduled) {
// Cancel an existing timeout.
cancelHostTimeout();
} else {
isHostTimeoutScheduled = true;
}
// Schedule a timeout.
//startTime - currentTime秒后执行handleTimeout,将timeQueue中的task推入taskQueue
requestHostTimeout(handleTimeout, startTime - currentTime);
}
}
3. taskQueue中task的调度过程
unstable_scheduleCallback
如果新建的是一个非延时任务,则按照expirationTime排序推入最小堆taskQueue,接着调用requestHostCallback(flushWork)
请求调度。
newTask.sortIndex = expirationTime;
push(taskQueue, newTask);
// Schedule a host callback, if needed. If we're already performing work,
// wait until the next time we yield.
if (!isHostCallbackScheduled && !isPerformingWork) {
isHostCallbackScheduled = true;
requestHostCallback(flushWork);
}
在flushWork
中主要是改变一些状态变量,接下来进入workLoop。
isHostCallbackScheduled = false;
if (isHostTimeoutScheduled) {
// We scheduled a timeout but it's no longer needed. Cancel it.
isHostTimeoutScheduled = false;
cancelHostTimeout();
}
isPerformingWork = true;
try{
return workLoop(hasTimeRemaining, initialTime);
}catch{
currentTask = null;
currentPriorityLevel = previousPriorityLevel;
isPerformingWork = false;
}
workLoop
函数通过while循环执行taskQueue中的任务,当当前优先级最高的任务的expiraTime < currentTime
并且当前时间片用完时,跳出workLoop循环,如果当前taskQueue中有更多的任务,则返回true等待下一次调度,否则返回false;
function workLoop(hasTimeRemaining, initialTime) {
let currentTime = initialTime;
advanceTimers(currentTime);
currentTask = peek(taskQueue);
while (
currentTask !== null &&
!(enableSchedulerDebugging && isSchedulerPaused)
) {
// This currentTask hasn't expired, and we've reached the deadline.
if (
currentTask.expirationTime > currentTime &&
(!hasTimeRemaining || shouldYieldToHost())
) {
break;
}
const callback = currentTask.callback;
if (callback !== null) {
currentTask.callback = null;
currentPriorityLevel = currentTask.priorityLevel;
const didUserCallbackTimeout = currentTask.expirationTime <= currentTime;
const continuationCallback = callback(didUserCallbackTimeout);
currentTime = getCurrentTime();
if (typeof continuationCallback === 'function') {
currentTask.callback = continuationCallback;
} else {
if (currentTask === peek(taskQueue)) {
pop(taskQueue);
}
}
advanceTimers(currentTime);
} else {
pop(taskQueue);
}
currentTask = peek(taskQueue);
}
// Return whether there's additional work
if (currentTask !== null) {
return true;
} else {
let firstTimer = peek(timerQueue);
if (firstTimer !== null) {
requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);
}
return false;
}
}
4 requestHostCallback
flushWork相当于将taskQueue中的任务包裹起来供requestHostCallback执行,真正对taskQueue中任务的分片执行逻辑在requestHostCallback中。
requestHostCallback = function(callback) {
scheduledHostCallback = callback;
if (!isMessageLoopRunning) {
isMessageLoopRunning = true;
port.postMessage(null);
}
};
flushWork传人后保存在全局变量scheduledHostCallback中,postMessage方法会创建一个宏任务,且给这个宏任务分配的默认执行时间是yieldInterval=5ms
, 这样每执行5ms就有机会将控制权交还给浏览器,从而不阻塞浏览器的UI渲染task,以及一些高优先级任务的入堆,使得即使在帧率较高的浏览器上也能保持较好的反应速度。
宏任务的执行在postMessage的onMessage回调中:
channel.port1.onmessage = performWorkUntilDeadline;
const performWorkUntilDeadline = () => {
if (scheduledHostCallback !== null) {
const currentTime = getCurrentTime();
// Yield after `yieldInterval` ms, regardless of where we are in the vsync
// cycle. This means there's always time remaining at the beginning of
// the message event.
deadline = currentTime + yieldInterval;
const hasTimeRemaining = true;
try {
const hasMoreWork = scheduledHostCallback(
hasTimeRemaining,
currentTime,
);
if (!hasMoreWork) {
isMessageLoopRunning = false;
scheduledHostCallback = null;
} else {
// If there's more work, schedule the next message event at the end of the preceding one.
port.postMessage(null);
}
} catch (error) {
// If a scheduler task throws, exit the current browser task so the
// error can be observed.
port.postMessage(null);
throw error;
}
} else {
isMessageLoopRunning = false;
}
// Yielding to the browser will give it a chance to paint, so we can
// reset this.
needsPaint = false;
};
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。