什么是饥饿问题: 高优先级一直打断低优先级,低优先级没有机会执行。
如何解决饥饿问题:
第一步: 在每次调度更新都会执行的 ensureRootIsScheduled 调用 markStarvedLanesAsExpired 函数。
function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
const existingCallbackNode = root.callbackNode;
// Check if any lanes are being starved by other work. If so, mark them as
// expired so we know to work on those next.
markStarvedLanesAsExpired(root, currentTime);
第二步: 在 markStarvedLanesAsExpired 函数,如果是第一次更新,那会遍历 root.pendingLane 上的 每一个 lane,针对于不同 lane 上的任务,通过调用 computeExpirationTime 为每一个任务计算过期时间。
function computeExpirationTime(lane: Lane, currentTime: number) {
switch (lane) {
case SyncHydrationLane:
case SyncLane:
case InputContinuousHydrationLane:
case InputContinuousLane:
return currentTime + 250;
case DefaultHydrationLane:
case DefaultLane:
case TransitionHydrationLane:
case TransitionLane1:
case TransitionLane16:
return currentTime + 5000;
case RetryLane4:
return NoTimestamp;
case SelectiveHydrationLane:
case IdleHydrationLane:
case IdleLane:
case OffscreenLane:
return NoTimestamp;
}
}
第三步: 下次调度更新再次来到markStarvedLanesAsExpired 函数,仍然去遍历每一条 lane, 对于新的更新任务还是计算过期时间,而对于哪些已经有过期时间的任务,去判断它们是否已经到期。如果已经到期 在 root.expiredLanes 上标记已经过期的 lane。
export 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 & ~RetryLanes;
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;
}
}
第四步:开始调度更新 performConcurrentWorkOnRoot 函数。
newCallbackNode = scheduleCallback(
schedulerPriorityLevel,
performConcurrentWorkOnRoot.bind(null, root),
);
第五步:在 performConcurrentWorkOnRoot 函数中,去通过 includesExpiredLane 函数 检查 root.expiredLanes 上是否有过期的 lane。如果有过期的 lane,也即一直被打断的低优先级赛道。那么会调用 renderRootSync 将低优先级直接提升至同步优先级进行渲染更新。低优先级任务得到了执行。
const shouldTimeSlice =
!includesBlockingLane(root, lanes) &&
!includesExpiredLane(root, lanes) &&
(disableSchedulerTimeoutInWorkLoop || !didTimeout);
let exitStatus = shouldTimeSlice
? renderRootConcurrent(root, lanes)
: renderRootSync(root, lanes);
自此解决饥饿问题