背景
目前 React 有一套scheduler 的调度器机制,通过scheduler 的调度,可以实现高优先级的任务优先调度,还有可恢复,可中断的特性。那么如何实现这一特性呢?其中一个关键的模型就是赛道模型。
事件优先级
React 本身对事件处理进行了包装,所以会存在事件优先级的概念,比如输入事件、点击事件等,都属于离散事件,优先级最高,需要同步执行。拖拽事件,滚动事件属于连续事件优先级,对应的都是连续事件优先级.
// 离散事件优先级, 为同步优先级0b0000000000000000000000000000001
// 比如click,input, change, blur, focus等
export const DiscreteEventPriority: EventPriority = SyncLane;
// 连续事件优先级, 为输入持续优先级0b0000000000000000000000000000100
// 比如touchmove, scroll,dragenter等
export const ContinuousEventPriority: EventPriority = InputContinuousLane;
// 默认事件优先级, 为0b0000000000000000000000000010000
export const DefaultEventPriority: EventPriority = DefaultLane;
// 空闲事件优先级, 为0b0100000000000000000000000000000
export const IdleEventPriority: EventPriority = IdleLane;
lane 优先级
lane 标识的是一个任务的优先级,总共有 31 种,直接看代码:
export const TotalLanes = 31;
export const NoLanes: Lanes = /* */ 0b0000000000000000000000000000000;
export const NoLane: Lane = /* */ 0b0000000000000000000000000000000;
export const SyncLane: Lane = /* */ 0b0000000000000000000000000000001;
export const InputContinuousHydrationLane: Lane = /* */ 0b0000000000000000000000000000010;
export const InputContinuousLane: Lane = /* */ 0b0000000000000000000000000000100;
export const DefaultHydrationLane: Lane = /* */ 0b0000000000000000000000000001000;
export const DefaultLane: Lane = /* */ 0b0000000000000000000000000010000;
const TransitionHydrationLane: Lane = /* */ 0b0000000000000000000000000100000;
const TransitionLanes: Lanes = /* */ 0b0000000001111111111111111000000;
const TransitionLane1: Lane = /* */ 0b0000000000000000000000001000000;
const TransitionLane2: Lane = /* */ 0b0000000000000000000000010000000;
const TransitionLane3: Lane = /* */ 0b0000000000000000000000100000000;
const TransitionLane4: Lane = /* */ 0b0000000000000000000001000000000;
const TransitionLane5: Lane = /* */ 0b0000000000000000000010000000000;
const TransitionLane6: Lane = /* */ 0b0000000000000000000100000000000;
const TransitionLane7: Lane = /* */ 0b0000000000000000001000000000000;
const TransitionLane8: Lane = /* */ 0b0000000000000000010000000000000;
const TransitionLane9: Lane = /* */ 0b0000000000000000100000000000000;
const TransitionLane10: Lane = /* */ 0b0000000000000001000000000000000;
const TransitionLane11: Lane = /* */ 0b0000000000000010000000000000000;
const TransitionLane12: Lane = /* */ 0b0000000000000100000000000000000;
const TransitionLane13: Lane = /* */ 0b0000000000001000000000000000000;
const TransitionLane14: Lane = /* */ 0b0000000000010000000000000000000;
const TransitionLane15: Lane = /* */ 0b0000000000100000000000000000000;
const TransitionLane16: Lane = /* */ 0b0000000001000000000000000000000;
const RetryLanes: Lanes = /* */ 0b0000111110000000000000000000000;
const RetryLane1: Lane = /* */ 0b0000000010000000000000000000000;
const RetryLane2: Lane = /* */ 0b0000000100000000000000000000000;
const RetryLane3: Lane = /* */ 0b0000001000000000000000000000000;
const RetryLane4: Lane = /* */ 0b0000010000000000000000000000000;
const RetryLane5: Lane = /* */ 0b0000100000000000000000000000000;
Schedule优先级
Schedule 总共有6 种优先级,直接看代码:
let ReactPriorityLevels: ReactPriorityLevelsType = {
ImmediatePriority: 99,
UserBlockingPriority: 98,
NormalPriority: 97,
LowPriority: 96,
IdlePriority: 95,
NoPriority: 90,
};
优先级的相互转换
lane优先级转换为事件优先级
function lanesToEventPriority(lanes) {
var lane = getHighestPriorityLane(lanes);
if (!isHigherEventPriority(DiscreteEventPriority, lane)) {
return DiscreteEventPriority;
}
if (!isHigherEventPriority(ContinuousEventPriority, lane)) {
return ContinuousEventPriority;
}
if (includesNonIdleWork(lane)) {
return DefaultEventPriority;
}
return IdleEventPriority;
}
事件优先级转换为Schedule优先级
function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
...
switch (lanesToEventPriority(nextLanes)) {
case DiscreteEventPriority:
schedulerPriorityLevel = ImmediateSchedulerPriority;
break;
case ContinuousEventPriority:
schedulerPriorityLevel = UserBlockingSchedulerPriority;
break;
case DefaultEventPriority:
schedulerPriorityLevel = NormalSchedulerPriority;
break;
case IdleEventPriority:
schedulerPriorityLevel = IdleSchedulerPriority;
break;
default:
schedulerPriorityLevel = NormalSchedulerPriority;
break;
}
}
如何工作
初始化lane
触发 React 更新一般只有两种方式:
- useState
- useReducer
下面用 useState 来看一下 lane 初始化的流程
function dispatchSetState(fiber, queue, action) {
// ...
var lane = requestUpdateLane(fiber);
// 生成一个 update 对象
var update = {
lane: lane,
action: action,
hasEagerState: false,
eagerState: null,
next: null,
};
// ...
}
从源码中可以看到通过requestUpdateLane生成了一个 lane 的赛道模型,然后将这个赛道模型包装到一个 update 对象(对应的就是一个任务)里。看一下requestUpdateLane的源码
function requestUpdateLane(fiber) {
// Special cases
var mode = fiber.mode;
// 非并发,产生同步优先级
if ((mode & ConcurrentMode) === NoMode) {
return SyncLane;
// 判断是否在 render 阶段产生的调度
} else if (
(executionContext & RenderContext) !== NoContext &&
workInProgressRootRenderLanes !== NoLanes
) {
// This is a render phase update. These are not officially supported. The
// old behavior is to give this the same "thread" (lanes) as
// whatever is currently rendering. So if you call `setState` on a component
// that happens later in the same render, it will flush. Ideally, we want to
// remove the special case and treat them as if they came from an
// interleaved event. Regardless, this pattern is not officially supported.
// This behavior is only a fallback. The flag only exists until we can roll
// out the setState warning, since existing code might accidentally rely on
// the current behavior.
return pickArbitraryLane(workInProgressRootRenderLanes);
}
// 判断是否为过渡优先级
var isTransition = requestCurrentTransition() !== NoTransition;
if (isTransition) {
// updates at the same priority within the same event. To do this, the
// inputs to the algorithm must be the same.
//
// The trick we use is to cache the first of each of these inputs within an
// event. Then reset the cached values once we can be sure the event is
// over. Our heuristic for that is whenever we enter a concurrent work loop.
if (currentEventTransitionLane === NoLane) {
// All transitions within the same event are assigned the same lane.
currentEventTransitionLane = claimNextTransitionLane();
}
return currentEventTransitionLane;
} // Updates originating inside certain React methods, like flushSync, have
// their priority set by tracking it with a context variable.
//
// The opaque type returned by the host config is internally a lane, so we can
// use that directly.
// TODO: Move this type conversion to the event priority module.
// 判断是否有手动设置优先级
var updateLane = getCurrentUpdatePriority();
if (updateLane !== NoLane) {
return updateLane;
} // This update originated outside React. Ask the host environment for an
// appropriate priority, based on the type of event.
//
// The opaque type returned by the host config is internally a lane, so we can
// use that directly.
// TODO: Move this type conversion to the event priority module.
// 找到事件优先级
var eventLane = getCurrentEventPriority();
return eventLane;
}
- 如果当前应用未开启并发模式,返回 SyncLane(同步更新) 。
- 是否为"render"阶段更新。
- 是否与transition相关,如果与 transition 相关,会计算优先级。
- 是否有"手动设置的优先级"
- 返回事件的优先级。
从上述代码中可以看到 React 会根据不同的情况返回不同的优先级,默认返回的是事件优先级。那相当于当我们在 click 事件中如果通过 setState 触发一个更新时,返回的就是事件优先级。
冒泡 Fiber
设置更新的节点是比较深的 FiberNode 节点,参与调度的节点是FiberRootNode,为了方便使用 lane,会向上冒泡,直到 FiberRootNode。完成这一功能的函数是markUpdateLaneFromFiberToRoot。
// 参与调度的是 FiberRootNode,产生 update 是某一个 fiberNode,需要将产生update 的 fiberNode 向上冒泡到 FiberRootNode,这样可以很方便的捕获和操作节点
function markUpdateLaneFromFiberToRoot(sourceFiber, lane) {
// Update the source fiber's lanes
/*KaSong*/ logHook(
"changeLanes",
"markUpdateLaneFromFiberToRoot",
sourceFiber.lanes,
mergeLanes(sourceFiber.lanes, lane)
);
sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);
var alternate = sourceFiber.alternate;
if (alternate !== null) {
alternate.lanes = mergeLanes(alternate.lanes, lane);
}
{
if (
alternate === null &&
(sourceFiber.flags & (Placement | Hydrating)) !== NoFlags
) {
warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);
}
} // Walk the parent path to the root and update the child lanes.
var node = sourceFiber;
var parent = sourceFiber.return;
// 每个祖先 fiberNode 都回附加 "源 fiberNode"选定的 lane
while (parent !== null) {
parent.childLanes = mergeLanes(parent.childLanes, lane);
alternate = parent.alternate;
if (alternate !== null) {
alternate.childLanes = mergeLanes(alternate.childLanes, lane);
} else {
{
if ((parent.flags & (Placement | Hydrating)) !== NoFlags) {
warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);
}
}
}
node = parent;
parent = parent.return;
}
if (node.tag === HostRoot) {
var root = node.stateNode;
return root;
} else {
return null;
}
}
- 在向上遍历的过程中,生成的 lane 会附加到每一级父fiberNode 的childLanes中(和completeWork类似,一个向上冒泡的过程)。
- 通过lane和childLanes,可以直接判断当前节点是否需要更新以及子孙节点是否需要更新,方便进行优化。
调度 FiberRootNode
通过getNextLanes处理 lanes 模型,返回适合的 lanes 优先级。
// 选择要调度的 lanes
function getNextLanes(root, wipLanes) {
// Early bailout if there's no pending work left.
var pendingLanes = root.pendingLanes;
if (pendingLanes === NoLanes) {
return NoLanes;
}
var nextLanes = NoLanes;
var suspendedLanes = root.suspendedLanes;
var pingedLanes = root.pingedLanes; // Do not work on any idle work until all the non-idle work has finished,
// even if the work is suspended.
// 获取非空闲的 lanes pendingLanes & NonIdleLanes; 将空闲任务从pendingLanes中分离出去
var nonIdlePendingLanes = pendingLanes & NonIdleLanes;
if (nonIdlePendingLanes !== NoLanes) {
// 除去挂起的任务
var nonIdleUnblockedLanes = nonIdlePendingLanes & ~suspendedLanes;
if (nonIdleUnblockedLanes !== NoLanes) {
nextLanes = getHighestPriorityLanes(nonIdleUnblockedLanes);
} else {
// 如果nonIdlePingedLanes为空,则从挂起的优先级中选择最高的优先级。 pingedLanes:由于请求成功,取消挂起的 update 对应的 lane。
var nonIdlePingedLanes = nonIdlePendingLanes & pingedLanes;
if (nonIdlePingedLanes !== NoLanes) {
nextLanes = getHighestPriorityLanes(nonIdlePingedLanes);
}
}
} else {
// The only remaining work is Idle.
// 从闲置的任务中继续上诉逻辑。
var unblockedLanes = pendingLanes & ~suspendedLanes;
if (unblockedLanes !== NoLanes) {
nextLanes = getHighestPriorityLanes(unblockedLanes);
} else {
if (pingedLanes !== NoLanes) {
nextLanes = getHighestPriorityLanes(pingedLanes);
}
}
}
if (nextLanes === NoLanes) {
// This should only be reachable if we're suspended
// TODO: Consider warning in this path if a fallback timer is not scheduled.
return NoLanes;
} // If we're already in the middle of a render, switching lanes will interrupt
// it and we'll lose our progress. We should only do this if the new lanes are
// higher priority.
if (
wipLanes !== NoLanes &&
wipLanes !== nextLanes && // If we already suspended with a delay, then interrupting is fine. Don't
// bother waiting until the root is complete.
(wipLanes & suspendedLanes) === NoLanes
) {
var nextLane = getHighestPriorityLane(nextLanes);
var wipLane = getHighestPriorityLane(wipLanes);
if (
// Tests whether the next lane is equal or lower priority than the wip
// one. This works because the bits decrease in priority as you go left.
nextLane >= wipLane || // Default priority updates should not interrupt transition updates. The
// only difference between default updates and transition updates is that
// default updates do not support refresh transitions.
(nextLane === DefaultLane && (wipLane & TransitionLanes) !== NoLanes)
) {
// Keep working on the existing in-progress tree. Do not interrupt.
return wipLanes;
}
}
if ((nextLanes & InputContinuousLane) !== NoLanes) {
// When updates are sync by default, we entangle continuous priority updates
// and default updates, so they render in the same batch. The only reason
// they use separate lanes is because continuous updates should interrupt
// transitions, but default updates should not.
nextLanes |= pendingLanes & DefaultLane;
} // Check for entangled lanes and add them to the batch.
//
// A lane is said to be entangled with another when it's not allowed to render
// in a batch that does not also include the other lane. Typically we do this
// when multiple updates have the same source, and we only want to respond to
// the most recent event from that source.
//
// Note that we apply entanglements *after* checking for partial work above.
// This means that if a lane is entangled during an interleaved event while
// it's already rendering, we won't interrupt it. This is intentional, since
// entanglement is usually "best effort": we'll try our best to render the
// lanes in the same batch, but it's not worth throwing out partially
// completed work in order to do it.
// TODO: Reconsider this. The counter-argument is that the partial work
// represents an intermediate state, which we don't want to show to the user.
// And by spending extra time finishing it, we're increasing the amount of
// time it takes to show the final state, which is what they are actually
// waiting for.
//
// For those exceptions where entanglement is semantically important, like
// useMutableSource, we should ensure that there is no partial work at the
// time we apply the entanglement.
var entangledLanes = root.entangledLanes;
if (entangledLanes !== NoLanes) {
var entanglements = root.entanglements;
var lanes = nextLanes & entangledLanes;
while (lanes > 0) {
var index = pickArbitraryLaneIndex(lanes);
var lane = 1 << index;
nextLanes |= entanglements[index];
/*KaSong*/ logHook(
"getNextLanes_entangledLanes",
entanglements[index]
);
lanes &= ~lane;
}
}
return nextLanes;
}
- 分离两类任务优先级,一类是闲置任务,一类是非闲置任务。
- 如果是非闲置任务
-
- 分离阻塞任务(一般是对应着Suspense产生的任务)
- 如果分离后的阻塞任务为空,从阻塞任务中选择最高优先级。
- 如果是闲置任务
-
- 分离阻塞任务(一般是对应着Suspense产生的任务)
- 如果分离后的阻塞任务为空,从阻塞任务中选择最高优先级
- 通过找到的 lane 根 wipLanes 进行比较,如果 wipLanes 比当前 lane 优先级高,直接用 wip 的,反之用 lane。
- 处理纠缠相关。
调度策略
function ensureRootIsScheduled(){
// ...
var nextLanes = getNextLanes(
root,
root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes
);
var newCallbackPriority = getHighestPriorityLane(nextLanes);
// 如果当前优先级和新的优先级一致,则复用当前任务,不需要进行调度
if (
existingCallbackPriority === newCallbackPriority && // Special case related to `act`. If the currently scheduled task is a
// Scheduler task, rather than an `act` task, cancel it and re-scheduled
// on the `act` queue.
!(
ReactCurrentActQueue$1.current !== null &&
existingCallbackNode !== fakeActCallbackNode
)
) {
{
// If we're going to re-use an existing task, it needs to exist.
// Assume that discrete update microtasks are non-cancellable and null.
// TODO: Temporary until we confirm this warning is not fired.
if (
existingCallbackNode == null &&
existingCallbackPriority !== SyncLane
) {
error(
"Expected scheduled callback to exist. This error is likely caused by a bug in React. Please file an issue."
);
}
} // The priority hasn't changed. We can reuse the existing task. Exit.
/*KaSong*/ logHook("priorityNotChange", newCallbackPriority);
return;
}
var newCallbackNode;
if (newCallbackPriority === SyncLane) {
// Special case: Sync React callbacks are scheduled on a special
// internal queue
if (root.tag === LegacyRoot) {
if (ReactCurrentActQueue$1.isBatchingLegacy !== null) {
ReactCurrentActQueue$1.didScheduleLegacyUpdate = true;
}
/*KaSong*/ logHook(
"scheduleCallback",
"legacySync",
performSyncWorkOnRoot.name
);
scheduleLegacySyncCallback(performSyncWorkOnRoot.bind(null, root));
} else {
/*KaSong*/ logHook(
"scheduleCallback",
"sync",
performSyncWorkOnRoot.name
);
scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));
}
{
// Flush the queue in a microtask.
if (ReactCurrentActQueue$1.current !== null) {
// Inside `act`, use our internal `act` queue so that these get flushed
// at the end of the current scope even when using the sync version
// of `act`.
ReactCurrentActQueue$1.current.push(flushSyncCallbacks);
} else {
/*KaSong*/ logHook(
"scheduleCallback",
"microtask",
flushSyncCallbacks.name
);
scheduleMicrotask(flushSyncCallbacks);
}
}
newCallbackNode = null;
} else {
var schedulerPriorityLevel;
switch (lanesToEventPriority(nextLanes)) {
case DiscreteEventPriority:
schedulerPriorityLevel = ImmediatePriority;
break;
case ContinuousEventPriority:
schedulerPriorityLevel = UserBlockingPriority;
break;
case DefaultEventPriority:
schedulerPriorityLevel = NormalPriority;
break;
case IdleEventPriority:
schedulerPriorityLevel = IdlePriority;
break;
default:
schedulerPriorityLevel = NormalPriority;
break;
}
/*KaSong*/ logHook(
"scheduleCallback",
"concurrent",
performConcurrentWorkOnRoot.name,
schedulerPriorityLevel
);
newCallbackNode = scheduleCallback$1(
schedulerPriorityLevel,
performConcurrentWorkOnRoot.bind(null, root)
);
}
}
- 通过获取的 lane 和现有优先级比较:
-
- 与现有优先级一致,中断当前新任务,重用之前任务。
- 新任务大于现有优先级,取消现有任务执行,优先执行优先级高的任务。
- 判断是否是同步更新,如果是同步更新,直接scheduleMicrotask(flushSyncCallbacks) 进行调度。
- 如果不是同步更新:
-
- 将最高优先级的 lane 调整为 Schedule 使用的优先级,使用 Scheduler调度回调函数(调度performConcurrentWorkOnRoot),在这个函数里可以使用可中断、可恢复、时间分片的特性。
处理饥饿问题
因为存在高优先级的任务可以打断低优先级的任务,可能会导致低优先级的任务一直不能运行的问题。所以需要一套饥饿任务的机制进行处理。
function performConcurrentWorkOnRoot(){
var shouldTimeSlice =
!includesBlockingLane(root, lanes) &&
!includesExpiredLane(root, lanes) &&
!didTimeout;
// var shouldTimeSlice = true;
/*KaSong*/ logHook("shouldTimeSlice", shouldTimeSlice);
var exitStatus = shouldTimeSlice
? renderRootConcurrent(root, lanes)
: renderRootSync(root, lanes);
}
performConcurrentWorkOnRoot函数是开启并发渲染的入口,通过源码可以看到通过shouldTimeSlice来启动并发渲染或同步渲染。shouldTimeSlice就是解决饥饿问题最关键的地方。
可以看到shouldTimeSlice由三个函数决定,这三个任务也决定着是调用同步渲染还是并发渲染。
function includesBlockingLane(root, lanes) {
var SyncDefaultLanes =
InputContinuousHydrationLane |
InputContinuousLane |
DefaultHydrationLane |
DefaultLane;
return (lanes & SyncDefaultLanes) !== NoLanes;
}
这个函数主要就是判断当前 lane 是否是包含阻塞的 lane,如果包含,则启动同步渲染。
function includesExpiredLane(root, lanes) {
// This is a separate check from includesBlockingLane because a lane can
// expire after a render has already started.
return (lanes & root.expiredLanes) !== NoLanes;
}
之前不是讲过markStarvedLanesAsExpired用来标记任务是否过期吗,如果过期,会在expiredLanes标记"过期的 lane"。
这里会判断 lane 是否过期,如果过期开启同步渲染,这样就不可以打断了。
didTimeout也可以控制是否采用同步渲染还是并发渲染。判断条件是计算出来的expirationTime是否小于当前时间,如果小于当前事件说明已经过期,直接同步执行即可。
总结
1. 事件优先级
React 将事件分为不同优先级,用于决定任务的执行顺序:
- 离散事件优先级(DiscreteEventPriority):优先级最高,同步执行。
-
- 例如:
click、input、change、blur、focus。 - 对应 Lane:
SyncLane。
- 例如:
- 连续事件优先级(ContinuousEventPriority):优先级次高。
-
- 例如:
touchmove、scroll、dragenter。 - 对应 Lane:
InputContinuousLane。
- 例如:
- 默认事件优先级(DefaultEventPriority):普通优先级。
-
- 对应 Lane:
DefaultLane。
- 对应 Lane:
- 空闲事件优先级(IdleEventPriority):优先级最低。
-
- 对应 Lane:
IdleLane。
- 对应 Lane:
2. Lane 优先级
Lane 是 React 用于表示任务优先级的模型,共有 31 种 Lane,优先级从高到低:
- SyncLane:同步优先级,最高优先级。
- InputContinuousLane:输入持续优先级。
- DefaultLane:默认优先级。
- TransitionLanes:过渡优先级,用于并发模式。
- RetryLanes:重试优先级。
- IdleLane:空闲优先级,最低优先级。
Lane 的优先级决定了任务的调度顺序,React 会根据任务的类型和上下文选择合适的 Lane。
3. 调度机制
React 的调度机制基于 Scheduler,总共有 6 种调度优先级:
- ImmediatePriority(99):最高优先级,同步执行。
- UserBlockingPriority(98):用户阻塞优先级。
- NormalPriority(97):普通优先级。
- LowPriority(96):低优先级。
- IdlePriority(95):空闲优先级。
- NoPriority(90):无优先级。
React 通过 lanesToEventPriority 将 Lane 优先级转换为事件优先级,再通过 ensureRootIsScheduled 将事件优先级转换为 Scheduler 优先级,最终决定任务的执行顺序。
4. Lane 的初始化与冒泡
- Lane 初始化:
-
- 通过
requestUpdateLane生成 Lane,优先级取决于当前上下文(如是否在并发模式、是否是过渡更新等)。 - 默认情况下,返回事件优先级(如
click事件会返回SyncLane)。
- 通过
- Lane 冒泡:
-
- 通过
markUpdateLaneFromFiberToRoot将 Lane 从触发更新的 Fiber 节点冒泡到 FiberRoot 节点。 - 在冒泡过程中,Lane 会被附加到每个父节点的
childLanes中,方便后续优化。
- 通过
5. 调度策略
- 任务调度:
-
- 通过
getNextLanes选择要调度的 Lane。 - 如果新任务的优先级与当前任务一致,则复用当前任务;否则,取消当前任务,优先执行高优先级任务。
- 通过
- 同步与并发渲染:
-
- 如果是同步任务(如
SyncLane),直接通过scheduleMicrotask调度。 - 如果是并发任务,将 Lane 转换为 Scheduler 优先级,通过
scheduleCallback调度。
- 如果是同步任务(如
6. 饥饿问题
由于高优先级任务可能会打断低优先级任务,导致低优先级任务一直无法执行,React 通过以下机制解决饥饿问题:
- includesBlockingLane:判断当前 Lane 是否包含阻塞任务,如果是,则同步执行。
- includesExpiredLane:判断当前 Lane 是否过期,如果是,则同步执行。
- didTimeout:判断任务是否超时,如果超时,则同步执行。
通过以上机制,React 确保低优先级任务不会一直被高优先级任务打断,最终得到执行。
- React 通过 事件优先级 和 Lane 优先级 管理任务的调度顺序。
- 调度机制 基于 Scheduler,将 Lane 优先级转换为 Scheduler 优先级,决定任务的执行方式(同步或并发)。
- 饥饿问题 通过判断任务的阻塞状态、过期状态和超时状态,确保低优先级任务最终得到执行。
这套机制使得 React 能够高效地处理用户交互和渲染任务,同时保证应用的响应性和性能。