跳到主要内容

React Fiber 根调度器

一、作用

二、导出的常量/变量

1. 首次调度根

// A linked list of all the roots with pending work. In an idiomatic app,
// there's only a single root, but we do support multi root apps, hence this
// extra complexity. But this module is optimized for the single root case.
//
// 所有有待处理工作的根节点的链表。在一个惯用的应用中,
// 通常只有一个根节点,但我们确实支持多根节点的应用,因此有了
// 额外的复杂性。不过这个模块针对单根节点的情况进行了优化。
export let firstScheduledRoot: FiberRoot | null = null;

三、确保根节点已调度

export function ensureRootIsScheduled(root: FiberRoot): void {
// This function is called whenever a root receives an update. It does two
// things 1) it ensures the root is in the root schedule, and 2) it ensures
// there's a pending microtask to process the root schedule.
//
// 每当根节点收到更新时都会调用此函数。它做两件事:
//
// - 确保根节点在根调度中
// - 确保有一个待处理的微任务来处理根调度。
//
// Most of the actual scheduling logic does not happen until
// `scheduleTaskForRootDuringMicrotask` runs.
//
// 大部分实际的调度逻辑要
// 等到 `scheduleTaskForRootDuringMicrotask` 运行时才会发生。

// Add the root to the schedule
// 将根添加到日程
if (root === lastScheduledRoot || root.next !== null) {
// Fast path. This root is already scheduled.
// 快速路径。该根节点已被调度。
} else {
if (lastScheduledRoot === null) {
firstScheduledRoot = lastScheduledRoot = root;
} else {
lastScheduledRoot.next = root;
lastScheduledRoot = root;
}
}

// Any time a root received an update, we set this to true until the next time
// we process the schedule. If it's false, then we can quickly exit flushSync
// without consulting the schedule.
//
// 每当根节点收到更新时,我们会将其设置为 true,直到下次处理调度为止。
// 如果为 false,那么我们可以快速退出 flushSync,而无需查看调度。
mightHavePendingSyncWork = true;

ensureScheduleIsScheduled();

if (
__DEV__ &&
!disableLegacyMode &&
ReactSharedInternals.isBatchingLegacy &&
root.tag === LegacyRoot
) {
// Special `act` case: Record whenever a legacy update is scheduled.
// 特殊 `act` 情况:记录每次计划的旧版更新。
ReactSharedInternals.didScheduleLegacyUpdate = true;
}
}

四、确保计划已安排

export function ensureScheduleIsScheduled(): void {
// At the end of the current event, go through each of the roots and ensure
// there's a task scheduled for each one at the correct priority.
//
// 在当前事件结束时,检查每个根节点,确保每个根节点都有按正确优先级安排的任务。
if (__DEV__ && ReactSharedInternals.actQueue !== null) {
// We're inside an `act` scope.
// 我们在一个 `act` 范围内。
if (!didScheduleMicrotask_act) {
didScheduleMicrotask_act = true;
scheduleImmediateRootScheduleTask();
}
} else {
if (!didScheduleMicrotask) {
didScheduleMicrotask = true;
scheduleImmediateRootScheduleTask();
}
}
}

五、在所有根上同步刷新工作

export function flushSyncWorkOnAllRoots() {
// This is allowed to be called synchronously, but the caller should check
// the execution context first.
//
// 允许同步调用,但调用者应先检查执行上下文。
flushSyncWorkAcrossRoots_impl(NoLanes, false);
}

六、 仅在旧版根上同步刷新工作

export function flushSyncWorkOnLegacyRootsOnly() {
// This is allowed to be called synchronously, but the caller should check
// the execution context first.
// 允许同步调用,但调用者应先检查执行上下文。
if (!disableLegacyMode) {
flushSyncWorkAcrossRoots_impl(NoLanes, true);
}
}

七、请求变 Lane

备注
export function requestTransitionLane(
// This argument isn't used, it's only here to encourage the caller to
// check that it's inside a transition before calling this function.
// TODO: Make this non-nullable. Requires a tweak to useOptimistic.
//
// 这个参数未被使用,它仅仅是为了提醒调用者在调用此函数之前检查它是否处于过渡状态。
// 待办事项:使其不可为空。需要对 useOptimistic 做一些调整。
transition: Transition | null,
): Lane {
// The algorithm for assigning an update to a lane should be stable for all
// 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.
// 同一事件内的所有转换都分配到相同的 Lane
const actionScopeLane = peekEntangledActionLane();
currentEventTransitionLane =
actionScopeLane !== NoLane
? // We're inside an async action scope. Reuse the same lane.
// 我们在一个异步操作范围内,重用相同的 Lane。
actionScopeLane
: // We may or may not be inside an async action scope. If we are, this
// is the first update in that scope. Either way, we need to get a
// fresh transition lane.
//
// 我们可能在异步操作作用域内,也可能不在。如果在的话,
// 这是该作用域内的第一次更新。不管怎样,我们都需要获取一个新的过渡 Lane 优先级。
claimNextTransitionUpdateLane();
}
return currentEventTransitionLane;
}

八、当前事件日程是否发生过转变

export function didCurrentEventScheduleTransition(): boolean {
return currentEventTransitionLane !== NoLane;
}

九、标记指示器已处理

备注
export function markIndicatorHandled(root: FiberRoot): void {
if (enableDefaultTransitionIndicator) {
// The current transition event rendered a synchronous loading state.
// Clear it from the indicator lanes. We don't need to show a separate
// loading state for this lane.
//
// 当前的过渡事件呈现了同步加载状态。
// 从指示器通道中清除它。我们不需要为此通道显示单独的加载状态。
root.indicatorLanes &= ~currentEventTransitionLane;
markIsomorphicIndicatorHandled();
}
}

十、变量

1. 上次调度器根

let lastScheduledRoot: FiberRoot | null = null;

2. 已调度微任务

// Used to prevent redundant mircotasks from being scheduled.
// 用于防止重复调度微任务。
let didScheduleMicrotask: boolean = false;
// `act` "microtasks" are scheduled on the `act` queue instead of an actual
// microtask, so we have to dedupe those separately. This wouldn't be an issue
// if we required all `act` calls to be awaited, which we might in the future.
//
// `act` 的“微任务”是安排在 `act` 队列上,而不是实际的微任务,
// 所以我们必须单独去重这些。如果我们要求所有 `act` 调用都必须被 await,
// 这本来不会是问题,未来我们可能会这么做。
let didScheduleMicrotask_act: boolean = false;

3. 可能有待处理的同步工作

// Used to quickly bail out of flushSync if there's no sync work to do.
// 如果没有同步工作要做,用于快速退出 flushSync。
let mightHavePendingSyncWork: boolean = false;

4. 正在刷新工作

let isFlushingWork: boolean = false;

5. 当前事件过渡 Lane

let currentEventTransitionLane: Lane = NoLane;

十一、工具

1. 立即调度根任务

备注
  • processRootScheduleInMicrotask()
  • scheduleMicrotask() 由构建平台自己实现
  • getExecutionContext()ReactFiberWorkLoop 中实现
function scheduleImmediateRootScheduleTask() {
if (__DEV__ && ReactSharedInternals.actQueue !== null) {
// Special case: Inside an `act` scope, we push microtasks to the fake `act`
// callback queue. This is because we currently support calling `act`
// without awaiting the result. The plan is to deprecate that, and require
// that you always await the result so that the microtasks have a chance to
// run. But it hasn't happened yet.
//
// 特殊情况:在 `act` 范围内,我们将微任务推入假的 `act` 回调队列。
// 这是因为我们目前支持调用 `act` 时不等待结果。
// 计划是弃用这种方式,并要求你始终等待结果,以便微任务有机会执行。
// 但这一计划尚未实施。
ReactSharedInternals.actQueue.push(() => {
processRootScheduleInMicrotask();
return null;
});
}

// TODO: Can we land supportsMicrotasks? Which environments don't support it?
// Alternatively, can we move this check to the host config?
// 待办事项:我们能实现支持 microtasks 吗?有哪些环境不支持它?
// 或者,我们能把这个检查移到主机配置中吗?
if (supportsMicrotasks) {
scheduleMicrotask(() => {
// In Safari, appending an iframe forces microtasks to run.
// https://github.com/facebook/react/issues/22459
// We don't support running callbacks in the middle of render
// or commit so we need to check against that.
// 在 Safari 中,附加 iframe 会强制微任务运行。
// https://github.com/facebook/react/issues/22459
// 我们不支持在渲染或提交过程中运行回调
// 因此我们需要进行相应的检查。
const executionContext = getExecutionContext();
if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
// Note that this would still prematurely flush the callbacks
// if this happens outside render or commit phase (e.g. in an event).
//
// 请注意,如果这发生在渲染或提交阶段之外(例如在事件中),这仍然会提前刷新回调。

// Intentionally using a macrotask instead of a microtask here. This is
// wrong semantically but it prevents an infinite loop. The bug is
// Safari's, not ours, so we just do our best to not crash even though
// the behavior isn't completely correct.
//
// 这里故意使用宏任务而不是微任务。
// 语义上这是错误的,但它可以防止无限循环。
// 这个问题出在 Safari,不是我们的问题,所以我们尽力避免崩溃,
// 即使行为不完全正确。
Scheduler_scheduleCallback(
ImmediateSchedulerPriority,
processRootScheduleInImmediateTask,
);
return;
}
processRootScheduleInMicrotask();
});
} else {
// If microtasks are not supported, use Scheduler.
// 如果不支持微任务,则使用调度器。
Scheduler_scheduleCallback(
ImmediateSchedulerPriority,
processRootScheduleInImmediateTask,
);
}
}

2. 在微任务中处理根调度

备注
function processRootScheduleInMicrotask() {
// This function is always called inside a microtask. It should never be
// called synchronously.
//
// 这个函数总是在微任务中被调用。它永远不应该被同步调用。
didScheduleMicrotask = false;
if (__DEV__) {
didScheduleMicrotask_act = false;
}

// We'll recompute this as we iterate through all the roots and schedule them.
// 我们将在遍历所有根并安排它们时重新计算这个值。
mightHavePendingSyncWork = false;

let syncTransitionLanes = NoLanes;
if (currentEventTransitionLane !== NoLane) {
if (shouldAttemptEagerTransition()) {
// A transition was scheduled during an event, but we're going to try to
// render it synchronously anyway. We do this during a popstate event to
// preserve the scroll position of the previous page.
//
// 在一个事件期间安排了过渡,但我们还是会尝试同步渲染它。
// 我们在 popstate 事件期间执行此操作,以保留上一页面的滚动位置。
syncTransitionLanes = currentEventTransitionLane;
} else if (enableDefaultTransitionIndicator) {
// If we have a Transition scheduled by this event it might be paired
// with Default lane scheduled loading indicators. To unbatch it from
// other events later on, flush it early to determine whether it
// rendered an indicator. This ensures that setState in default priority
// event doesn't trigger onDefaultTransitionIndicator.
//
// 如果我们有由此事件调度的过渡,它可能会与默认通道调度的加载指示器配对。
// 为了稍后将其与其他事件分离,需要提前刷新它,以确定它是否渲染了指示器。
// 这可以确保默认优先级事件中的 setState 不会触发 onDefaultTransitionIndicator。
syncTransitionLanes = DefaultLane;
}
}

const currentTime = now();

let prev = null;
let root = firstScheduledRoot;
while (root !== null) {
const next = root.next;
// scheduleTaskForRootDuringMicrotask() 本文档实现
const nextLanes = scheduleTaskForRootDuringMicrotask(root, currentTime);
if (nextLanes === NoLane) {
// This root has no more pending work. Remove it from the schedule. To
// guard against subtle reentrancy bugs, this microtask is the only place
// we do this — you can add roots to the schedule whenever, but you can
// only remove them here.
//
// 这个根节点没有更多待处理的工作。将其从调度中移除。
// 为防止微妙的重入错误,这个微任务是我们唯一执行此操作的地方——你可以随时
// 将根节点添加到调度中,但只能在这里移除它们。

// Null this out so we know it's been removed from the schedule.
// 将其置空,以便我们知道它已从日程中删除。
root.next = null;
if (prev === null) {
// This is the new head of the list
// 这是列表的新头
firstScheduledRoot = next;
} else {
prev.next = next;
}
if (next === null) {
// This is the new tail of the list
// 这是列表的新尾部
lastScheduledRoot = prev;
}
} else {
// This root still has work. Keep it in the list.
// 这个根节点仍然有工作。将它保留在列表中。
prev = root;

// This is a fast-path optimization to early exit from
// flushSyncWorkOnAllRoots if we can be certain that there is no remaining
// synchronous work to perform. Set this to true if there might be sync
// work left.
//
// 这是一个快速路径优化,用于在我们可以确定没有剩余的同步工作需要执行时,提前
// 退出 flushSyncWorkOnAllRoots。如果可能还有同步工作未完成,请将其设置为 true。
if (
// Skip the optimization if syncTransitionLanes is set
// 如果设置了 syncTransitionLanes,则跳过优化
syncTransitionLanes !== NoLanes ||
// Common case: we're not treating any extra lanes as synchronous, so we
// can just check if the next lanes are sync.
//
// 常见情况:我们不将任何额外的线程视为同步线程,所以我们
// 只需检查下一个线程是否是同步的即可。
includesSyncLane(nextLanes) ||
(enableGestureTransition && isGestureRender(nextLanes))
) {
mightHavePendingSyncWork = true;
}
}
root = next;
}

// At the end of the microtask, flush any pending synchronous work. This has
// to come at the end, because it does actual rendering work that might throw.
// If we're in the middle of a View Transition async sequence, we don't want to
// interrupt that sequence. Instead, we'll flush any remaining work when it
// completes.
//
// 在微任务结束时,刷新任何未完成的同步工作。
// 这必须放在最后,因为它会执行实际的渲染工作,可能会抛出异常。
// 如果我们正处于视图过渡异步序列中,我们不想中断该序列。
// 相反,我们将在序列完成后刷新任何剩余的工作。
if (!hasPendingCommitEffects()) {
// 本文档实现该方法
flushSyncWorkAcrossRoots_impl(syncTransitionLanes, false);
}

if (currentEventTransitionLane !== NoLane) {
// Reset Event Transition Lane so that we allocate a new one next time.
// 重置事件切换通道,以便下次分配一个新的通道。
currentEventTransitionLane = NoLane;
// 本文档实现该方法
startDefaultTransitionIndicatorIfNeeded();
}
}

3. 在即时任务中处理根调度

备注
  • trackSchedulerEvent() 由构建平台自己实现
function processRootScheduleInImmediateTask() {
if (enableProfilerTimer && enableComponentPerformanceTrack) {
// Track the currently executing event if there is one so we can ignore this
// event when logging events.
//
// 跟踪当前正在执行的事件(如果有),以便在记录事件时可以忽略此事件。
trackSchedulerEvent();
}

processRootScheduleInMicrotask();
}

4.在所有根节点中同步刷新工作(实现)

备注
function flushSyncWorkAcrossRoots_impl(
syncTransitionLanes: Lanes | Lane,
onlyLegacy: boolean,
) {
if (isFlushingWork) {
// Prevent reentrancy.
// TODO: Is this overly defensive? The callers must check the execution
// context first regardless.
//
// 防止重入。
// 待办事项:这是否过于谨慎?调用者无论如何都必须先检查执行上下文。
return;
}

if (!mightHavePendingSyncWork) {
// Fast path. There's no sync work to do.
// 快速路径。没有需要同步的工作。
return;
}

// There may or may not be synchronous work scheduled. Let's check.
// 可能有同步工作安排,也可能没有。让我们检查一下。
let didPerformSomeWork;
isFlushingWork = true;
do {
didPerformSomeWork = false;
let root = firstScheduledRoot;
while (root !== null) {
if (onlyLegacy && (disableLegacyMode || root.tag !== LegacyRoot)) {
// Skip non-legacy roots.
// 跳过非遗留根。
} else {
if (syncTransitionLanes !== NoLanes) {
const nextLanes = getNextLanesToFlushSync(root, syncTransitionLanes);
if (nextLanes !== NoLanes) {
// This root has pending sync work. Flush it now.
// 该根有待同步的工作。现在刷新它。
didPerformSomeWork = true;
performSyncWorkOnRoot(root, nextLanes); // 本文档实现
}
} else {
const workInProgressRoot = getWorkInProgressRoot();
const workInProgressRootRenderLanes =
getWorkInProgressRootRenderLanes();
const rootHasPendingCommit =
root.cancelPendingCommit !== null ||
root.timeoutHandle !== noTimeout;
const nextLanes = getNextLanes(
root,
root === workInProgressRoot
? workInProgressRootRenderLanes
: NoLanes,
rootHasPendingCommit,
);
if (
(includesSyncLane(nextLanes) ||
(enableGestureTransition && isGestureRender(nextLanes))) &&
!checkIfRootIsPrerendering(root, nextLanes)
) {
// This root has pending sync work. Flush it now.
// 该根有待同步的工作。现在刷新它。
didPerformSomeWork = true;
performSyncWorkOnRoot(root, nextLanes);
}
}
}
root = root.next;
}
} while (didPerformSomeWork);
isFlushingWork = false;
}

5. 在根节点执行同步工作

备注
function performSyncWorkOnRoot(root: FiberRoot, lanes: Lanes) {
// This is the entry point for synchronous tasks that don't go
// through Scheduler.
//
// 这是用于不经过调度器的同步任务的入口点。
const didFlushPassiveEffects = flushPendingEffects();
if (didFlushPassiveEffects) {
// If passive effects were flushed, exit to the outer work loop in the root
// scheduler, so we can recompute the priority.
//
// 如果被动效果已被清除,则退出到根调度器中的外部工作循环,以便我们可以重新计算优先级。
return null;
}
if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {
syncNestedUpdateFlag();
}
const forceSync = true;
performWorkOnRoot(root, lanes, forceSync);
}

6. 在微任务期间为根调度任务

备注
function scheduleTaskForRootDuringMicrotask(
root: FiberRoot,
currentTime: number,
): Lane {
// This function is always called inside a microtask, or at the very end of a
// rendering task right before we yield to the main thread. It should never be
// called synchronously.
//
// 这个函数总是在微任务中调用,或者在渲染任务的最后阶段、我们
// 将控制权交回主线程之前调用。它不应被同步调用。

// This function also never performs React work synchronously; it should
// only schedule work to be performed later, in a separate task or microtask.
//
// 这个函数也绝不会同步执行 React 工作;它只应安排稍后在单独任务或微任务中执行的工作。

// Check if any lanes are being starved by other work. If so, mark them as
// expired so we know to work on those next.
//
// 检查是否有 Lane 被其他任务饿死。
// 如果有,将其标记为过期,这样我们就知道接下来要处理这些 Lane
markStarvedLanesAsExpired(root, currentTime);

// Determine the next lanes to work on, and their priority.
// 确定接下来要处理的车道及其优先级。
const rootWithPendingPassiveEffects = getRootWithPendingPassiveEffects();
const pendingPassiveEffectsLanes = getPendingPassiveEffectsLanes();
const workInProgressRoot = getWorkInProgressRoot();
const workInProgressRootRenderLanes = getWorkInProgressRootRenderLanes();
const rootHasPendingCommit =
root.cancelPendingCommit !== null || root.timeoutHandle !== noTimeout;
const nextLanes =
enableYieldingBeforePassive && root === rootWithPendingPassiveEffects
? // This will schedule the callback at the priority of the lane but we used to
// always schedule it at NormalPriority. Discrete will flush it sync anyway.
// So the only difference is Idle and it doesn't seem necessarily right for that
// to get upgraded beyond something important just because we're past commit.
//
// 这将按照通道的优先级调度回调,但我们以前
// 总是以 NormalPriority 调度它。离散任务无论如何都会同步刷新。
// 因此,唯一的区别是空闲任务,而且仅仅因为我们已经提交,
// 就把它升级为比重要的任务还高,这似乎不太对。
pendingPassiveEffectsLanes
: getNextLanes(
root,
root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,
rootHasPendingCommit,
);

const existingCallbackNode = root.callbackNode;
if (
// Check if there's nothing to work on
// Check if there's nothing to work on
nextLanes === NoLanes ||
// If this root is currently suspended and waiting for data to resolve, don't
// schedule a task to render it. We'll either wait for a ping, or wait to
// receive an update.
//
// 如果这个根当前处于挂起状态并等待数据解析,不要调度任务来渲染它。
// 我们要么等待 ping,要么等待接收更新。
//
// Suspended render phase
// 挂起的渲染阶段
(root === workInProgressRoot && isWorkLoopSuspendedOnData()) ||
// Suspended commit phase
// 挂起的提交阶段
root.cancelPendingCommit !== null
) {
// Fast path: There's nothing to work on.
// 快速路径:没有需要处理的内容。
if (existingCallbackNode !== null) {
cancelCallback(existingCallbackNode);
}
root.callbackNode = null;
root.callbackPriority = NoLane;
return NoLane;
}

// Schedule a new callback in the host environment.
// 在宿主环境中安排一个新的回调。
if (
includesSyncLane(nextLanes) &&
// If we're prerendering, then we should use the concurrent work loop
// even if the lanes are synchronous, so that prerendering never blocks
// the main thread.
//
// 如果我们在进行预渲染,那么我们应该使用并发工作循环
// 即使这些通道是同步的,这样预渲染也不会阻塞
// 主线程。
!checkIfRootIsPrerendering(root, nextLanes)
) {
// Synchronous work is always flushed at the end of the microtask, so we
// don't need to schedule an additional task.
//
// 同步工作总是在微任务结束时被刷新,因此我们不需要安排额外的任务。
if (existingCallbackNode !== null) {
cancelCallback(existingCallbackNode);
}
root.callbackPriority = SyncLane;
root.callbackNode = null;
return SyncLane;
} else {
// We use the highest priority lane to represent the priority of the callback.
// 我们使用最高优先级的通道来表示回调的优先级。
const existingCallbackPriority = root.callbackPriority;
const newCallbackPriority = getHighestPriorityLane(nextLanes);

if (
newCallbackPriority === existingCallbackPriority &&
// Special case related to `act`. If the currently scheduled task is a
// Scheduler task, rather than an `act` task, cancel it and re-schedule
// on the `act` queue.
//
// 与 `act` 相关的特殊情况。如果当前计划的任务是
// 调度器任务,而不是 `act` 任务,则取消它并重新在
// `act` 队列上调度。
!(
__DEV__ &&
ReactSharedInternals.actQueue !== null &&
existingCallbackNode !== fakeActCallbackNode
)
) {
// The priority hasn't changed. We can reuse the existing task.
// 优先级没有变化。我们可以重复使用现有任务。
return newCallbackPriority;
} else {
// Cancel the existing callback. We'll schedule a new one below.
// 取消现有的回调。我们将在下面安排一个新的回调。
cancelCallback(existingCallbackNode);
}

let schedulerPriorityLevel;
switch (lanesToEventPriority(nextLanes)) {
// Scheduler does have an "ImmediatePriority", but now that we use
// microtasks for sync work we no longer use that. Any sync work that
// reaches this path is meant to be time sliced.
//
// 调度器确实有一个“ImmediatePriority”,但现在我们使用微任务来处理同步
// 工作,所以不再使用它。任何到达此路径的同步工作都应该进行时间切片处理。
case DiscreteEventPriority:
case ContinuousEventPriority:
schedulerPriorityLevel = UserBlockingSchedulerPriority;
break;
case DefaultEventPriority:
schedulerPriorityLevel = NormalSchedulerPriority;
break;
case IdleEventPriority:
schedulerPriorityLevel = IdleSchedulerPriority;
break;
default:
schedulerPriorityLevel = NormalSchedulerPriority;
break;
}

const newCallbackNode = scheduleCallback(
schedulerPriorityLevel,
performWorkOnRootViaSchedulerTask.bind(null, root),
);

root.callbackPriority = newCallbackPriority;
root.callbackNode = newCallbackNode;
return newCallbackPriority;
}
}

7. 取消回调

备注
function cancelCallback(callbackNode: mixed) {
if (__DEV__ && callbackNode === fakeActCallbackNode) {
// Special `act` case: check if this is the fake callback node used by
// the `act` implementation.
} else if (callbackNode !== null) {
Scheduler_cancelCallback(callbackNode);
}
}

8. 调度器回调

备注
function scheduleCallback(
priorityLevel: PriorityLevel,
callback: RenderTaskFn,
) {
if (__DEV__ && ReactSharedInternals.actQueue !== null) {
// Special case: We're inside an `act` scope (a testing utility).
// Instead of scheduling work in the host environment, add it to a
// fake internal queue that's managed by the `act` implementation.
//
// 特殊情况:我们在 `act` 范围内(一个测试工具)。
// 不在主环境中调度工作,而是将其添加到由 `act` 实现管理的假内部队列中。
ReactSharedInternals.actQueue.push(callback);
return fakeActCallbackNode;
} else {
return Scheduler_scheduleCallback(priorityLevel, callback);
}
}

9. 通过调度任务在根上执行工作

备注
type RenderTaskFn = (didTimeout: boolean) => RenderTaskFn | null;

function performWorkOnRootViaSchedulerTask(
root: FiberRoot,
didTimeout: boolean,
): RenderTaskFn | null {
// This is the entry point for concurrent tasks scheduled via Scheduler (and
// postTask, in the future).
//
// 这是通过调度器(以及将来的 postTask)调度的并发任务的入口点。

if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {
resetNestedUpdateFlag();
}

if (enableProfilerTimer && enableComponentPerformanceTrack) {
// Track the currently executing event if there is one so we can ignore this
// event when logging events.
//
// 跟踪当前正在执行的事件(如果有的话),以便在记录事件时忽略此事件。
trackSchedulerEvent();
}

if (hasPendingCommitEffects()) {
// We are currently in the middle of an async committing (such as a View Transition).
// We could force these to flush eagerly but it's better to defer any work until
// it finishes. This may not be the same root as we're waiting on.
// TODO: This relies on the commit eventually calling ensureRootIsScheduled which
// always calls processRootScheduleInMicrotask which in turn always loops through
// all the roots to figure out. This is all a bit inefficient and if optimized
// it'll need to consider rescheduling a task for any skipped roots.
//
// 我们当前正处于异步提交的中间(例如视图转换)。
// 我们可以强制这些立即刷新,但最好等到完成后再进行任何工作。
// 这可能不是我们正在等待的同一个根。
// TODO:这依赖于提交最终调用 ensureRootIsScheduled,该方法总是
// 调用 processRootScheduleInMicrotask,
// 而 processRootScheduleInMicrotask 又总是循环遍历所有根来确定。
// 这一切都有点低效,如果优化的话,需要考虑为任何被跳过的根重新调度任务。
root.callbackNode = null;
root.callbackPriority = NoLane;
return null;
}

// Flush any pending passive effects before deciding which lanes to work on,
// in case they schedule additional work.
//
// 在决定要处理哪些通道之前,刷新任何挂起的被动效果,
// 以防它们安排了额外的工作。
const originalCallbackNode = root.callbackNode;
const didFlushPassiveEffects = flushPendingEffectsDelayed();
if (didFlushPassiveEffects) {
// Something in the passive effect phase may have canceled the current task.
// Check if the task node for this root was changed.
//
// 被动效果阶段的某些因素可能取消了当前任务。
// 检查此根任务的任务节点是否已更改。
if (root.callbackNode !== originalCallbackNode) {
// The current task was canceled. Exit. We don't need to call
// `ensureRootIsScheduled` because the check above implies either that
// there's a new task, or that there's no remaining work on this root.
//
// 当前任务已被取消。退出。我们不需要调用
// `ensureRootIsScheduled`,因为上面的检查意味着要么有新任务,
// 要么这个根节点上没有剩余的工作。
return null;
} else {
// Current task was not canceled. Continue.
// 当前任务未被取消。继续。
}
}

// Determine the next lanes to work on, using the fields stored on the root.
// TODO: We already called getNextLanes when we scheduled the callback; we
// should be able to avoid calling it again by stashing the result on the
// root object. However, because we always schedule the callback during
// a microtask (scheduleTaskForRootDuringMicrotask), it's possible that
// an update was scheduled earlier during this same browser task (and
// therefore before the microtasks have run). That's because Scheduler batches
// together multiple callbacks into a single browser macrotask, without
// yielding to microtasks in between. We should probably change this to align
// with the postTask behavior (and literally use postTask when
// it's available).
//
// 使用存储在根节点上的字段来确定接下来要处理的 lanes。
// TODO: 我们在调度回调时已经调用过 getNextLanes;
// 我们应该通过将结果存储在根对象上来避免再次调用它。
// 但是,由于我们总是在微任务期间调度回调(scheduleTaskForRootDuringMicrotask),
// 所以可能在同一个浏览器任务期间更早地安排过更新(因此在微任务运行之前)。
// 这是因为 Scheduler 会将多个回调批量处理到单个浏览器宏任务中,中间不会让出控制权给微任务。
// 我们可能应该修改这一点,使其与 postTask 行为一致(并在可用时真正使用 postTask)。
const workInProgressRoot = getWorkInProgressRoot();
const workInProgressRootRenderLanes = getWorkInProgressRootRenderLanes();
const rootHasPendingCommit =
root.cancelPendingCommit !== null || root.timeoutHandle !== noTimeout;
const lanes = getNextLanes(
root,
root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,
rootHasPendingCommit,
);
if (lanes === NoLanes) {
// No more work on this root.
// 不再处理这个根。
return null;
}

// Enter the work loop.
// TODO: We only check `didTimeout` defensively, to account for a Scheduler
// bug we're still investigating. Once the bug in Scheduler is fixed,
// we can remove this, since we track expiration ourselves.
//
// 进入工作循环。
// TODO: 我们只是在防御性地检查 `didTimeout`,以应对我们仍在调查的 Scheduler 缺陷。
// 一旦 Scheduler 中的缺陷修复后,我们可以移除这个检查,因为我们会自己跟踪过期时间。
const forceSync = !disableSchedulerTimeoutInWorkLoop && didTimeout;
performWorkOnRoot(root, lanes, forceSync);

// The work loop yielded, but there may or may not be work left at the current
// priority. Need to determine whether we need to schedule a continuation.
// Usually `scheduleTaskForRootDuringMicrotask` only runs inside a microtask;
// however, since most of the logic for determining if we need a continuation
// versus a new task is the same, we cheat a bit and call it here. This is
// only safe to do because we know we're at the end of the browser task.
// So although it's not an actual microtask, it might as well be.
//
// 工作循环已经让出,但当前优先级下可能还有工作,也可能没有。需要确定是否需要调度一个后续任务。
// 通常 `scheduleTaskForRootDuringMicrotask` 只在微任务内运行;
// 不过,由于判断是否需要后续任务还是新任务的大部分逻辑是相同的,我们在这里略微“作弊”调用它。
// 之所以安全,是因为我们知道此时处于浏览器任务的末尾。
// 所以尽管这并不是一个实际的微任务,它也可以当作是微任务。
scheduleTaskForRootDuringMicrotask(root, now());
if (root.callbackNode != null && root.callbackNode === originalCallbackNode) {
// The task node scheduled for this root is the same one that's
// currently executed. Need to return a continuation.
//
// 为该根节点安排的任务节点与当前执行的节点相同。需要返回一个延续。
return performWorkOnRootViaSchedulerTask.bind(null, root);
}
return null;
}

10. 如有必要,启动默认过渡指示器

备注
function startDefaultTransitionIndicatorIfNeeded() {
if (!enableDefaultTransitionIndicator) {
return;
}
// Check if we need to start an isomorphic indicator like if an async action
// was started.
// 检查是否需要启动同构指示器,例如是否启动了异步操作。
startIsomorphicDefaultIndicatorIfNeeded();
// Check all the roots if there are any new indicators needed.
// 检查所有根目录,看看是否需要任何新的指示器。
let root = firstScheduledRoot;
while (root !== null) {
if (root.indicatorLanes !== NoLanes && root.pendingIndicator === null) {
// We have new indicator lanes that requires a loading state. Start the
// default transition indicator.
//
// 我们有新的指示器通道需要加载状态。启动默认的过渡指示器。
if (hasOngoingIsomorphicIndicator()) {
// We already have an isomorphic indicator going which means it has to
// also apply to this root since it implies all roots have the same one.
// We retain this indicator so that it keeps going until we commit this
// root.
//
// 我们已经有一个同构指标在运行,这意味着它也必须应用于这个根,因为
// 这意味着所有根都有相同的指标。
// 我们保留这个指标,以便它在我们提交这个根之前继续运行。
root.pendingIndicator = retainIsomorphicIndicator();
} else {
try {
const onDefaultTransitionIndicator =
root.onDefaultTransitionIndicator;
root.pendingIndicator = onDefaultTransitionIndicator() || noop;
} catch (x) {
root.pendingIndicator = noop;
reportGlobalError(x);
}
}
}
root = root.next;
}
}