React 的优先级调度机制和打断处理
#react
目录
1. 总结
总结一下 React 防止低优先级任务饥饿的主要机制:
- 过期时间:每个任务都有过期时间,==一旦过期会被强制执行==
- 优先级提升:
- 长时间得不到执行的低优任务会被提升优先级
- 时间片轮转:
- 通过==时间片划分==,确保任务能够交替执行
- ==动态优先级==:
- 任务优先级不是固定的,会根据等待时间动态调整
- 批量更新:
- 相同优先级的更新会被合并,提高执行效率
这种机制确保了即使在==高优先级任务频繁插入的情况下==,低优先级任务最终也能得到执行,同时又不会影响用户交互的流畅性。
2. 优先级打断机制
// 简化的优先级定义
const priorities = {
ImmediatePriority: 1, // 最高优先级,同步任务
UserBlockingPriority: 2, // 用户交互
NormalPriority: 3, // 普通优先级
LowPriority: 4, // 低优先级
IdlePriority: 5 // 空闲优先级
}
打断过程如下:
function workLoop(deadline) {
// 是否有待处理的高优先级任务
while (currentTask !== null && !shouldYield()) {
try {
// 如果有更高优先级任务进来
if (hasHigherPriorityWork()) {
// 中断当前工作
return suspendCurrentWork();
}
performUnitOfWork(currentTask);
} catch (err) {
// 错误处理
}
}
}
3. 饥饿问题解决方案
React 通过以下机制来防止低优先级任务饥饿:
3.1. 过期时间机制
function scheduleCallback(priorityLevel, callback) {
const currentTime = getCurrentTime();
// 根据优先级计算过期时间
const timeout = getTimeoutByPriorityLevel(priorityLevel);
const expirationTime = currentTime + timeout;
const newTask = {
callback,
priorityLevel,
expirationTime,
startTime: currentTime,
};
// 将任务加入队列
push(taskQueue, newTask);
}
// 不同优先级的超时时间
const timeoutForPriorityLevel = {
[ImmediatePriority]: -1, // 立即执行
[UserBlockingPriority]: 250,
[NormalPriority]: 5000,
[LowPriority]: 10000,
[IdlePriority]: maxSigned31BitInt
};
3.2. 任务饥饿检测
function shouldYieldToHost() {
const currentTime = getCurrentTime();
// 检查是否有过期任务
if (peek(taskQueue) !== null) {
const expiredTask = peek(taskQueue);
if (expiredTask.expirationTime <= currentTime) {
// 如果任务过期,强制执行
return true;
}
}
return false;
}
4. 优先级提升机制
function markTaskExpired(task) {
const currentTime = getCurrentTime();
if (currentTime >= task.expirationTime) {
// 过期任务提升优先级
task.priorityLevel = ImmediatePriority;
}
}
5. 时间片轮转
function workLoopConcurrent() {
// 当有工作要做且时间片未用完
while (workInProgress !== null && !shouldYield()) {
workInProgress = performUnitOfWork(workInProgress);
}
}
function shouldYield() {
// 检查时间片是否用完
const currentTime = getCurrentTime();
return currentTime >= deadline;
}
6. 任务队列管理
class Scheduler {
constructor() {
this.taskQueue = new MinHeap(); // 普通任务队列
this.timerQueue = new MinHeap(); // 延时任务队列
}
scheduleTask(task) {
const currentTime = getCurrentTime();
if (task.startTime <= currentTime) {
// 立即执行的任务
this.taskQueue.push(task);
} else {
// 延时任务
this.timerQueue.push(task);
}
}
// 获取下一个要执行的任务
getNextTask() {
const currentTime = getCurrentTime();
// 将到期的延时任务移到普通队列
this.advanceTimers(currentTime);
// 返回最高优先级的任务
return this.taskQueue.peek();
}
}
7. 可中断渲染的实现
function renderRoot(root, isYieldy) {
// 是否可以中断
isWorkLoopConcurrent = isYieldy;
do {
try {
workLoopConcurrent();
} catch (thrownValue) {
handleError(root, thrownValue);
}
} while (workInProgress !== null && !shouldYield());
// 如果还有未完成的工作
if (workInProgress !== null) {
// 返回未完成标记
return RootIncomplete;
}
// 所有工作完成
return RootCompleted;
}
8. 优先级继承
function scheduleUpdateOnFiber(fiber, lane) {
const root = markUpdateLaneFromFiberToRoot(fiber);
if (lane === SyncLane) {
// 同步优先级
ensureRootIsScheduled(root);
} else {
// 异步优先级
scheduleCallback(
schedulerPriorityLevel,
() => {
performConcurrentWorkOnRoot(root);
}
);
}
}
9. 批量更新处理
function batchedUpdates(fn) {
const prevExecutionContext = executionContext;
executionContext |= BatchedContext;
try {
return fn();
} finally {
executionContext = prevExecutionContext;
// 如果没有其他工作正在进行,刷新同步队列
if (executionContext === NoContext) {
flushSyncCallbackQueue();
}
}
}