Appearance
Fiber调度原理 - React任务调度系统深度剖析
1. 调度系统概述
1.1 为什么需要调度
typescript
const schedulingNeed = {
问题背景: {
浏览器限制: '单线程JavaScript执行',
性能目标: '60fps,每帧16.6ms',
现实挑战: 'React渲染可能超过一帧时间',
用户体验: '长任务导致卡顿、掉帧'
},
解决方案: {
任务分割: '将大任务拆分成小任务',
时间切片: '每个小任务5ms左右',
优先级: '重要任务优先执行',
可中断: '随时暂停和恢复'
},
核心目标: [
'保持界面流畅(60fps)',
'快速响应用户交互',
'合理分配CPU时间',
'避免任务饥饿'
]
};1.2 调度架构
typescript
const schedulerArchitecture = {
层次结构: {
React层: {
responsibility: '管理组件更新',
api: 'scheduleUpdateOnFiber',
priority: 'Lane优先级模型'
},
Scheduler层: {
responsibility: '任务调度和时间切片',
api: 'scheduleCallback',
priority: 'Priority优先级模型'
},
浏览器层: {
responsibility: '执行任务',
api: 'MessageChannel/setTimeout',
mechanism: '宏任务队列'
}
},
工作流程: `
1. React触发更新
2. 根据Lane确定优先级
3. 转换为Scheduler优先级
4. 调度任务到Scheduler
5. Scheduler选择合适时机执行
6. 执行Fiber工作循环
7. 检查是否需要让出控制权
8. 继续或暂停任务
`
};1.3 优先级体系
typescript
// React的Lane优先级
const enum ReactPriority {
NoLane = 0,
SyncLane = 1, // 同步优先级
InputContinuousLane = 2, // 连续输入
DefaultLane = 4, // 默认优先级
TransitionLane = 8, // 过渡优先级
IdleLane = 16 // 空闲优先级
}
// Scheduler的优先级
const enum SchedulerPriority {
ImmediatePriority = 1, // 立即执行
UserBlockingPriority = 2, // 用户阻塞
NormalPriority = 3, // 普通优先级
LowPriority = 4, // 低优先级
IdlePriority = 5 // 空闲优先级
}
// 优先级转换
function lanesToSchedulerPriority(lanes: Lanes): SchedulerPriority {
const lane = getHighestPriorityLane(lanes);
if (includesSyncLane(lane)) {
return ImmediatePriority;
}
if (includesInputContinuousLane(lane)) {
return UserBlockingPriority;
}
if (includesDefaultLane(lane)) {
return NormalPriority;
}
if (includesTransitionLane(lane)) {
return LowPriority;
}
return IdlePriority;
}2. Scheduler核心实现
2.1 任务队列
typescript
// 任务对象
interface Task {
id: number;
callback: (() => void) | null;
priorityLevel: SchedulerPriority;
startTime: number;
expirationTime: number;
sortIndex: number;
}
// 小顶堆实现
class MinHeap<T> {
private heap: T[] = [];
private compare: (a: T, b: T) => boolean;
constructor(compare: (a: T, b: T) => boolean) {
this.compare = compare;
}
push(node: T): void {
const heap = this.heap;
const index = heap.length;
heap.push(node);
this.siftUp(index);
}
peek(): T | null {
return this.heap[0] || null;
}
pop(): T | null {
const heap = this.heap;
if (heap.length === 0) {
return null;
}
const first = heap[0];
const last = heap.pop()!;
if (heap.length > 0) {
heap[0] = last;
this.siftDown(0);
}
return first;
}
private siftUp(index: number): void {
const heap = this.heap;
const node = heap[index];
while (index > 0) {
const parentIndex = (index - 1) >>> 1;
const parent = heap[parentIndex];
if (this.compare(parent, node)) {
break;
}
heap[index] = parent;
index = parentIndex;
}
heap[index] = node;
}
private siftDown(index: number): void {
const heap = this.heap;
const length = heap.length;
const node = heap[index];
while (true) {
const leftIndex = (index + 1) * 2 - 1;
const rightIndex = leftIndex + 1;
if (leftIndex >= length) {
break;
}
const left = heap[leftIndex];
const right = rightIndex < length ? heap[rightIndex] : null;
let minIndex = leftIndex;
let minNode = left;
if (right && this.compare(right, left)) {
minIndex = rightIndex;
minNode = right;
}
if (this.compare(node, minNode)) {
break;
}
heap[index] = minNode;
index = minIndex;
}
heap[index] = node;
}
}
// 任务队列
const taskQueue = new MinHeap<Task>((a, b) =>
a.sortIndex < b.sortIndex
);
// 延迟任务队列
const timerQueue = new MinHeap<Task>((a, b) =>
a.startTime < b.startTime
);2.2 调度任务
typescript
let taskIdCounter = 1;
let currentTask: Task | null = null;
let currentPriorityLevel = NormalPriority;
let isSchedulerPaused = false;
let isPerformingWork = false;
let isHostCallbackScheduled = false;
/**
* 调度回调
*/
function scheduleCallback(
priorityLevel: SchedulerPriority,
callback: () => boolean | void,
options?: { delay?: number }
): Task {
const currentTime = getCurrentTime();
let startTime: number;
if (options?.delay) {
startTime = currentTime + options.delay;
} else {
startTime = currentTime;
}
// 计算过期时间
let timeout: number;
switch (priorityLevel) {
case ImmediatePriority:
timeout = -1; // 立即过期
break;
case UserBlockingPriority:
timeout = 250;
break;
case IdlePriority:
timeout = 1073741823; // 永不过期
break;
case LowPriority:
timeout = 10000;
break;
case NormalPriority:
default:
timeout = 5000;
break;
}
const expirationTime = startTime + timeout;
const newTask: Task = {
id: taskIdCounter++,
callback,
priorityLevel,
startTime,
expirationTime,
sortIndex: -1
};
if (startTime > currentTime) {
// 延迟任务
newTask.sortIndex = startTime;
timerQueue.push(newTask);
if (taskQueue.peek() === null && newTask === timerQueue.peek()) {
// 如果是最早的延迟任务,设置定时器
requestHostTimeout(handleTimeout, startTime - currentTime);
}
} else {
// 立即任务
newTask.sortIndex = expirationTime;
taskQueue.push(newTask);
// 调度工作
if (!isHostCallbackScheduled && !isPerformingWork) {
isHostCallbackScheduled = true;
requestHostCallback(flushWork);
}
}
return newTask;
}
/**
* 取消任务
*/
function cancelCallback(task: Task): void {
task.callback = null;
}2.3 执行任务
typescript
/**
* 刷新工作
*/
function flushWork(hasTimeRemaining: boolean, initialTime: number): boolean {
isHostCallbackScheduled = false;
isPerformingWork = true;
const previousPriorityLevel = currentPriorityLevel;
try {
// 将到期的延迟任务移到任务队列
advanceTimers(initialTime);
// 执行任务队列
return workLoop(hasTimeRemaining, initialTime);
} finally {
currentTask = null;
currentPriorityLevel = previousPriorityLevel;
isPerformingWork = false;
}
}
/**
* 工作循环
*/
function workLoop(hasTimeRemaining: boolean, initialTime: number): boolean {
let currentTime = initialTime;
advanceTimers(currentTime);
currentTask = taskQueue.peek();
while (currentTask !== null) {
if (
currentTask.expirationTime > currentTime &&
(!hasTimeRemaining || shouldYieldToHost())
) {
// 任务未过期且需要让出控制权
break;
}
const callback = currentTask.callback;
if (typeof callback === 'function') {
currentTask.callback = null;
currentPriorityLevel = currentTask.priorityLevel;
const didUserCallbackTimeout = currentTask.expirationTime <= currentTime;
// 执行任务
const continuationCallback = callback(didUserCallbackTimeout);
currentTime = getCurrentTime();
if (typeof continuationCallback === 'function') {
// 任务返回了continuation,继续
currentTask.callback = continuationCallback;
} else {
// 任务完成,移除
if (currentTask === taskQueue.peek()) {
taskQueue.pop();
}
}
advanceTimers(currentTime);
} else {
// 任务已取消
taskQueue.pop();
}
currentTask = taskQueue.peek();
}
// 返回是否还有更多工作
if (currentTask !== null) {
return true;
} else {
// 检查延迟任务
const firstTimer = timerQueue.peek();
if (firstTimer !== null) {
requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);
}
return false;
}
}
/**
* 推进定时器
*/
function advanceTimers(currentTime: number): void {
let timer = timerQueue.peek();
while (timer !== null) {
if (timer.callback === null) {
// 已取消
timerQueue.pop();
} else if (timer.startTime <= currentTime) {
// 到期,移到任务队列
timerQueue.pop();
timer.sortIndex = timer.expirationTime;
taskQueue.push(timer);
} else {
// 未到期
return;
}
timer = timerQueue.peek();
}
}3. 时间切片实现
3.1 shouldYield判断
typescript
// 帧间隔(5ms)
let yieldInterval = 5;
let deadline = 0;
/**
* 是否应该让出控制权
*/
function shouldYieldToHost(): boolean {
const currentTime = getCurrentTime();
return currentTime >= deadline;
}
/**
* 强制让出
*/
function forceFrameRate(fps: number): void {
if (fps < 0 || fps > 125) {
console.error('fps must be between 0 and 125');
return;
}
if (fps > 0) {
yieldInterval = Math.floor(1000 / fps);
} else {
yieldInterval = 5;
}
}
/**
* 获取当前时间
*/
function getCurrentTime(): number {
return performance.now();
}3.2 宿主环境调度
typescript
// 使用MessageChannel实现
let schedulePerformWorkUntilDeadline: () => void;
if (typeof MessageChannel !== 'undefined') {
const channel = new MessageChannel();
const port = channel.port2;
channel.port1.onmessage = () => {
if (scheduledHostCallback !== null) {
const currentTime = getCurrentTime();
deadline = currentTime + yieldInterval;
const hasTimeRemaining = true;
try {
const hasMoreWork = scheduledHostCallback(hasTimeRemaining, currentTime);
if (!hasMoreWork) {
isMessageLoopRunning = false;
scheduledHostCallback = null;
} else {
// 还有工作,继续调度
port.postMessage(null);
}
} catch (error) {
// 重新抛出错误
port.postMessage(null);
throw error;
}
} else {
isMessageLoopRunning = false;
}
};
schedulePerformWorkUntilDeadline = () => {
port.postMessage(null);
};
} else {
// 降级到setTimeout
schedulePerformWorkUntilDeadline = () => {
setTimeout(() => {
if (scheduledHostCallback !== null) {
const currentTime = getCurrentTime();
deadline = currentTime + yieldInterval;
const hasTimeRemaining = true;
try {
const hasMoreWork = scheduledHostCallback(hasTimeRemaining, currentTime);
if (hasMoreWork) {
schedulePerformWorkUntilDeadline();
} else {
isMessageLoopRunning = false;
scheduledHostCallback = null;
}
} catch (error) {
schedulePerformWorkUntilDeadline();
throw error;
}
}
}, 0);
};
}
let scheduledHostCallback: ((hasTimeRemaining: boolean, currentTime: number) => boolean) | null = null;
let isMessageLoopRunning = false;
/**
* 请求宿主回调
*/
function requestHostCallback(callback: (hasTimeRemaining: boolean, currentTime: number) => boolean): void {
scheduledHostCallback = callback;
if (!isMessageLoopRunning) {
isMessageLoopRunning = true;
schedulePerformWorkUntilDeadline();
}
}3.3 延迟任务处理
typescript
let taskTimeoutID = -1;
/**
* 请求宿主超时
*/
function requestHostTimeout(callback: () => void, ms: number): void {
taskTimeoutID = setTimeout(() => {
callback();
}, ms);
}
/**
* 取消宿主超时
*/
function cancelHostTimeout(): void {
clearTimeout(taskTimeoutID);
taskTimeoutID = -1;
}
/**
* 处理超时
*/
function handleTimeout(currentTime: number): void {
advanceTimers(currentTime);
if (!isHostCallbackScheduled) {
if (taskQueue.peek() !== null) {
isHostCallbackScheduled = true;
requestHostCallback(flushWork);
} else {
const firstTimer = timerQueue.peek();
if (firstTimer !== null) {
requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);
}
}
}
}4. React调度集成
4.1 ensureRootIsScheduled
typescript
/**
* 确保Root被调度
*/
function ensureRootIsScheduled(root: FiberRoot, currentTime: number): void {
const existingCallbackNode = root.callbackNode;
// 标记饥饿的lanes为过期
markStarvedLanesAsExpired(root, currentTime);
// 获取下一个要处理的lanes
const nextLanes = getNextLanes(
root,
root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes
);
if (nextLanes === NoLanes) {
// 没有工作,取消现有调度
if (existingCallbackNode !== null) {
cancelCallback(existingCallbackNode);
}
root.callbackNode = null;
root.callbackPriority = NoLane;
return;
}
// 获取最高优先级lane
const newCallbackPriority = getHighestPriorityLane(nextLanes);
const existingCallbackPriority = root.callbackPriority;
if (existingCallbackPriority === newCallbackPriority) {
// 优先级相同,复用现有调度
return;
}
// 取消现有调度
if (existingCallbackNode !== null) {
cancelCallback(existingCallbackNode);
}
// 调度新任务
let newCallbackNode;
if (newCallbackPriority === SyncLane) {
// 同步优先级
scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));
if (supportsMicrotasks) {
scheduleMicrotask(flushSyncCallbacks);
} else {
scheduleCallback(ImmediatePriority, flushSyncCallbacks);
}
newCallbackNode = null;
} else {
// 异步优先级
let schedulerPriorityLevel = lanesToSchedulerPriority(newCallbackPriority);
newCallbackNode = scheduleCallback(
schedulerPriorityLevel,
performConcurrentWorkOnRoot.bind(null, root)
);
}
root.callbackNode = newCallbackNode;
root.callbackPriority = newCallbackPriority;
}4.2 并发工作
typescript
/**
* 执行并发工作
*/
function performConcurrentWorkOnRoot(
root: FiberRoot,
didTimeout: boolean
): boolean | void {
// 获取要处理的lanes
const originalCallbackNode = root.callbackNode;
// 检查是否有过期任务
const didFlushPassiveEffects = flushPassiveEffects();
if (didFlushPassiveEffects) {
// passive effects可能调度了新的更新
if (root.callbackNode !== originalCallbackNode) {
return null;
}
}
let lanes = getNextLanes(
root,
root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes
);
if (lanes === NoLanes) {
return null;
}
// 检查是否应该同步渲染
const shouldTimeSlice =
!includesBlockingLane(root, lanes) &&
!includesExpiredLane(root, lanes) &&
!didTimeout;
let exitStatus = shouldTimeSlice
? renderRootConcurrent(root, lanes)
: renderRootSync(root, lanes);
if (exitStatus !== RootInProgress) {
if (exitStatus === RootErrored) {
// 错误处理
const errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);
if (errorRetryLanes !== NoLanes) {
lanes = errorRetryLanes;
exitStatus = recoverFromConcurrentError(root, errorRetryLanes);
}
}
if (exitStatus === RootFatalErrored) {
const fatalError = workInProgressRootFatalError;
prepareFreshStack(root, NoLanes);
markRootSuspended(root, lanes);
ensureRootIsScheduled(root, now());
throw fatalError;
}
if (exitStatus === RootDidNotComplete) {
// 渲染未完成,标记为suspended
markRootSuspended(root, lanes);
} else {
// 渲染完成
const finishedWork = root.current.alternate;
root.finishedWork = finishedWork;
root.finishedLanes = lanes;
finishConcurrentRender(root, exitStatus, lanes);
}
}
// 确保被调度
ensureRootIsScheduled(root, now());
// 返回continuation
if (root.callbackNode === originalCallbackNode) {
return performConcurrentWorkOnRoot.bind(null, root);
}
return null;
}4.3 同步工作
typescript
/**
* 执行同步工作
*/
function performSyncWorkOnRoot(root: FiberRoot): void {
// 获取lanes
const lanes = getNextLanes(root, NoLanes);
if (!includesSomeLane(lanes, SyncLane)) {
// 没有同步工作
ensureRootIsScheduled(root, now());
return;
}
let exitStatus = renderRootSync(root, lanes);
if (root.tag !== LegacyRoot && exitStatus === RootErrored) {
// 错误恢复
const errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);
if (errorRetryLanes !== NoLanes) {
lanes = errorRetryLanes;
exitStatus = recoverFromConcurrentError(root, errorRetryLanes);
}
}
if (exitStatus === RootFatalErrored) {
const fatalError = workInProgressRootFatalError;
prepareFreshStack(root, NoLanes);
markRootSuspended(root, lanes);
ensureRootIsScheduled(root, now());
throw fatalError;
}
if (exitStatus === RootDidNotComplete) {
throw new Error('Root did not complete');
}
// 完成渲染
const finishedWork = root.current.alternate;
root.finishedWork = finishedWork;
root.finishedLanes = lanes;
commitRoot(root);
ensureRootIsScheduled(root, now());
}5. 批量更新
5.1 批量更新机制
typescript
// 批量更新上下文
let executionContext = NoContext;
const enum ExecutionContext {
NoContext = 0b000,
BatchedContext = 0b001,
RenderContext = 0b010,
CommitContext = 0b100
}
/**
* 批量更新
*/
function batchedUpdates<A, R>(fn: (a: A) => R, a: A): R {
const prevExecutionContext = executionContext;
executionContext |= BatchedContext;
try {
return fn(a);
} finally {
executionContext = prevExecutionContext;
if (executionContext === NoContext) {
// 批量更新结束,刷新同步队列
flushSyncCallbacksOnlyInLegacyMode();
}
}
}
/**
* 离散更新
*/
function discreteUpdates<A, B, C, D, R>(
fn: (a: A, b: B, c: C, d: D) => R,
a: A,
b: B,
c: C,
d: D
): R {
const prevExecutionContext = executionContext;
executionContext |= DiscreteEventContext;
try {
return runWithPriority(
UserBlockingPriority,
fn.bind(null, a, b, c, d)
);
} finally {
executionContext = prevExecutionContext;
if (executionContext === NoContext) {
flushSyncCallbacksOnlyInLegacyMode();
}
}
}5.2 同步回调队列
typescript
// 同步回调队列
let syncQueue: Array<() => void> | null = null;
let isFlushingSyncQueue = false;
/**
* 调度同步回调
*/
function scheduleSyncCallback(callback: () => void): void {
if (syncQueue === null) {
syncQueue = [callback];
} else {
syncQueue.push(callback);
}
}
/**
* 刷新同步回调
*/
function flushSyncCallbacks(): void {
if (!isFlushingSyncQueue && syncQueue !== null) {
isFlushingSyncQueue = true;
try {
const queue = syncQueue;
runWithPriority(ImmediatePriority, () => {
for (let i = 0; i < queue.length; i++) {
let callback = queue[i];
do {
callback = callback();
} while (callback !== null);
}
});
syncQueue = null;
} finally {
isFlushingSyncQueue = false;
}
}
}6. 优先级计算
6.1 Lane选择
typescript
/**
* 获取下一个lanes
*/
function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes {
const pendingLanes = root.pendingLanes;
if (pendingLanes === NoLanes) {
return NoLanes;
}
let nextLanes = NoLanes;
let nextLanePriority = NoLanePriority;
const expiredLanes = root.expiredLanes;
const suspendedLanes = root.suspendedLanes;
const pingedLanes = root.pingedLanes;
// 检查过期lanes
const nonIdlePendingLanes = pendingLanes & NonIdleLanes;
if (nonIdlePendingLanes !== NoLanes) {
const nonIdleUnblockedLanes = nonIdlePendingLanes & ~suspendedLanes;
if (nonIdleUnblockedLanes !== NoLanes) {
nextLanes = getHighestPriorityLanes(nonIdleUnblockedLanes);
nextLanePriority = return_highestLanePriority;
} else {
const nonIdlePingedLanes = nonIdlePendingLanes & pingedLanes;
if (nonIdlePingedLanes !== NoLanes) {
nextLanes = getHighestPriorityLanes(nonIdlePingedLanes);
nextLanePriority = return_highestLanePriority;
}
}
} else {
// 只有idle lanes
const unblockedLanes = pendingLanes & ~suspendedLanes;
if (unblockedLanes !== NoLanes) {
nextLanes = getHighestPriorityLanes(unblockedLanes);
nextLanePriority = return_highestLanePriority;
} else {
if (pingedLanes !== NoLanes) {
nextLanes = getHighestPriorityLanes(pingedLanes);
nextLanePriority = return_highestLanePriority;
}
}
}
if (nextLanes === NoLanes) {
return NoLanes;
}
// 包含wipLanes中与nextLanes相同或更高优先级的lanes
if (wipLanes !== NoLanes && wipLanes !== nextLanes) {
const wipLanePriority = getHighestPriorityLane(wipLanes);
if (nextLanePriority <= wipLanePriority) {
return wipLanes;
}
}
// 包含被纠缠的lanes
const entangledLanes = root.entangledLanes;
if (entangledLanes !== NoLanes) {
const entanglements = root.entanglements;
let lanes = nextLanes & entangledLanes;
while (lanes > 0) {
const index = pickArbitraryLaneIndex(lanes);
const lane = 1 << index;
nextLanes |= entanglements[index];
lanes &= ~lane;
}
}
return nextLanes;
}
/**
* 获取最高优先级lanes
*/
function getHighestPriorityLanes(lanes: Lanes): Lanes {
// 同步lane
if ((lanes & SyncLane) !== NoLanes) {
return_highestLanePriority = SyncLanePriority;
return SyncLane;
}
// 输入连续lane
const inputContinuousLanes = InputContinuousLanes & lanes;
if (inputContinuousLanes !== NoLanes) {
return_highestLanePriority = InputContinuousLanePriority;
return inputContinuousLanes;
}
// 默认lane
const defaultLanes = DefaultLanes & lanes;
if (defaultLanes !== NoLanes) {
return_highestLanePriority = DefaultLanePriority;
return defaultLanes;
}
// 过渡lanes
const transitionLanes = TransitionLanes & lanes;
if (transitionLanes !== NoLanes) {
return_highestLanePriority = TransitionPriority;
return transitionLanes;
}
// Idle lanes
if ((lanes & IdleLanes) !== NoLanes) {
return_highestLanePriority = IdleLanePriority;
return lanes & IdleLanes;
}
return_highestLanePriority = DefaultLanePriority;
return lanes;
}6.2 Lane纠缠
typescript
/**
* 标记纠缠的lanes
*/
function markRootEntangled(root: FiberRoot, entangledLanes: Lanes): void {
const rootEntangledLanes = root.entangledLanes |= entangledLanes;
const entanglements = root.entanglements;
let lanes = rootEntangledLanes;
while (lanes > 0) {
const index = pickArbitraryLaneIndex(lanes);
const lane = 1 << index;
if (
(lane & entangledLanes) |
(entanglements[index] & entangledLanes)
) {
entanglements[index] |= entangledLanes;
}
lanes &= ~lane;
}
}
/**
* 标记root更新
*/
function markRootUpdated(
root: FiberRoot,
updateLane: Lane,
eventTime: number
): void {
root.pendingLanes |= updateLane;
if (updateLane !== IdleLane) {
root.suspendedLanes = NoLanes;
root.pingedLanes = NoLanes;
}
const eventTimes = root.eventTimes;
const index = laneToIndex(updateLane);
eventTimes[index] = eventTime;
}7. 饥饿处理
7.1 过期时间
typescript
/**
* 标记饥饿lanes为过期
*/
function markStarvedLanesAsExpired(
root: FiberRoot,
currentTime: number
): void {
const pendingLanes = root.pendingLanes;
const suspendedLanes = root.suspendedLanes;
const pingedLanes = root.pingedLanes;
const expirationTimes = root.expirationTimes;
let lanes = pendingLanes;
while (lanes > 0) {
const index = pickArbitraryLaneIndex(lanes);
const lane = 1 << index;
const expirationTime = expirationTimes[index];
if (expirationTime === NoTimestamp) {
if (
(lane & suspendedLanes) === NoLanes ||
(lane & pingedLanes) !== NoLanes
) {
// 设置过期时间
expirationTimes[index] = computeExpirationTime(lane, currentTime);
}
} else if (expirationTime <= currentTime) {
// 已过期
root.expiredLanes |= lane;
}
lanes &= ~lane;
}
}
/**
* 计算过期时间
*/
function computeExpirationTime(lane: Lane, currentTime: number): number {
const priority = getHighestPriorityLane(lane);
switch (priority) {
case SyncLane:
return currentTime + 250;
case InputContinuousLane:
return currentTime + 250;
case DefaultLane:
return currentTime + 5000;
case TransitionLane:
return currentTime + 10000;
case IdleLane:
return NoTimestamp;
default:
return NoTimestamp;
}
}8. 性能优化
8.1 任务合并
typescript
/**
* 合并任务
*/
function mergeTasks(task1: Task, task2: Task): Task {
// 取较高优先级
const priorityLevel = task1.priorityLevel < task2.priorityLevel
? task1.priorityLevel
: task2.priorityLevel;
// 取较早过期时间
const expirationTime = task1.expirationTime < task2.expirationTime
? task1.expirationTime
: task2.expirationTime;
// 合并回调
const mergedCallback = (didTimeout: boolean) => {
task1.callback && task1.callback(didTimeout);
task2.callback && task2.callback(didTimeout);
};
return {
id: taskIdCounter++,
callback: mergedCallback,
priorityLevel,
startTime: getCurrentTime(),
expirationTime,
sortIndex: expirationTime
};
}8.2 性能监控
typescript
/**
* 性能分析
*/
function profileScheduler() {
const stats = {
totalTasks: 0,
completedTasks: 0,
cancelledTasks: 0,
avgExecutionTime: 0,
avgWaitTime: 0
};
// 包装scheduleCallback
const originalSchedule = scheduleCallback;
scheduleCallback = function(priority, callback, options) {
stats.totalTasks++;
const startTime = getCurrentTime();
const wrappedCallback = (didTimeout: boolean) => {
const executeTime = getCurrentTime();
const waitTime = executeTime - startTime;
const result = callback(didTimeout);
const endTime = getCurrentTime();
const executionTime = endTime - executeTime;
stats.completedTasks++;
stats.avgWaitTime =
(stats.avgWaitTime * (stats.completedTasks - 1) + waitTime) /
stats.completedTasks;
stats.avgExecutionTime =
(stats.avgExecutionTime * (stats.completedTasks - 1) + executionTime) /
stats.completedTasks;
return result;
};
return originalSchedule(priority, wrappedCallback, options);
};
return stats;
}9. 面试高频问题
typescript
const schedulerInterviewQA = {
Q1: {
question: 'React的调度系统如何工作?',
answer: [
'1. React触发更新,分配Lane优先级',
'2. 转换为Scheduler优先级',
'3. 将任务加入优先级队列',
'4. Scheduler选择合适时机执行',
'5. 时间切片,可中断渲染',
'6. 完成或暂停,等待下次调度'
]
},
Q2: {
question: '时间切片如何实现?',
answer: `
1. 使用MessageChannel(或setTimeout)调度宏任务
2. 每5ms检查是否超时
3. 超时则让出控制权
4. 浏览器处理其他任务(渲染、交互)
5. 下一帧继续执行
`
},
Q3: {
question: 'React如何处理不同优先级的更新?',
answer: [
'1. Lane模型: 用二进制位表示优先级',
'2. 高优先级中断低优先级',
'3. 饥饿处理: 低优先级超时提升',
'4. 批量更新: 合并同优先级',
'5. 过期lanes立即执行'
]
},
Q4: {
question: 'Scheduler的任务队列如何实现?',
answer: `
使用最小堆(MinHeap):
1. 按expirationTime排序
2. peek()获取最早过期任务
3. O(1)查找,O(logN)插入/删除
4. 延迟任务单独队列
`
},
Q5: {
question: '为什么使用MessageChannel而非setTimeout?',
answer: [
'1. setTimeout最小延迟4ms(嵌套5层后)',
'2. MessageChannel没有延迟限制',
'3. MessageChannel是宏任务,不阻塞微任务',
'4. 性能更好,更精确'
]
},
Q6: {
question: '批量更新如何实现?',
answer: `
1. executionContext标记批量上下文
2. 在事件处理器中自动批量
3. 收集多个setState
4. 批量结束后统一调度
5. React 18自动批量(包括异步)
`
}
};10. 总结
Fiber调度系统的核心要点:
- 调度架构: React层 -> Scheduler层 -> 浏览器层
- 优先级: Lane模型和Scheduler优先级转换
- 任务队列: 最小堆实现的优先级队列
- 时间切片: 5ms间隔,MessageChannel调度
- 可中断: workLoop检查shouldYield
- 批量更新: executionContext管理批量
- 饥饿处理: 过期时间防止低优先级饥饿
- 性能优化: 任务合并、优先级提升
理解调度系统是掌握React性能优化的关键。