[源码文件](https://github.com/facebook/react/blob/a152827ef697c55f89926f9b6b7aa436f1c0504e/packages/scheduler/src/Scheduler.js)
var maxSigned31BitInt = 1073741823; // Times out immediately var IMMEDIATE_PRIORITY_TIMEOUT = -1; // Eventually times out var USER_BLOCKING_PRIORITY = 250; var NORMAL_PRIORITY_TIMEOUT = 5000; var LOW_PRIORITY_TIMEOUT = 10000; // Never times out var IDLE_PRIORITY = maxSigned31BitInt;当有更新任务来的时候,不会马上去做 Diff 操作,而是先把当前的更新送入一个 Update Queue 中,然后交给 Scheduler 去处理,Scheduler 会根据当前主线程的使用情况去处理这次 Update。
不管执行的过程怎样拆分、以什么顺序执行,Fiber 都会保证状态的一致性和视图的一致性。
如何保证相同在一定时间内触发的优先级一样的任务到期时间相同? React 通过 ceiling 方法来实现的。。。本菜没使用过 | 语法...
下面是处理到期时间的 ceiling 源码
[源码文件](https://github.com/facebook/react/blob/a152827ef697c55f89926f9b6b7aa436f1c0504e/packages/scheduler/src/Scheduler.js)
function ceiling(num, precision) { return (((num / precision) | 0) + 1) * precision; }那么为什么需要保证时间一致性?请看下文。
Fiber 如何调度?首先要找到调度入口地址 scheduleUpdateOnFiber,
每一个root都有一个唯一的调度任务,如果已经存在,我们要确保到期时间与下一级别任务的相同(所以用上文提到的 ceiling 方法来控制到期时间)
源码文件
export function scheduleUpdateOnFiber( fiber: Fiber, expirationTime: ExpirationTime, ) { checkForNestedUpdates(); warnAboutRenderPhaseUpdatesInDEV(fiber); // 调用markUpdateTimeFromFiberToRoot,更新 fiber 节点的 expirationTime // ps 此时的fiber树只有一个root fiber。 const root = markUpdateTimeFromFiberToRoot(fiber, expirationTime); if (root === null) { warnAboutUpdateOnUnmountedFiberInDEV(fiber); return; } // TODO: computeExpirationForFiber also reads the priority. Pass the // priority as an argument to that function and this one. // 还只是TODO // computeExpirationForFiber还会读取优先级。 // 将优先级作为参数传递给该函数和该函数。 const priorityLevel = getCurrentPriorityLevel(); if (expirationTime === Sync) { if ( // Check if we're inside unbatchedUpdates // 检查是否在未批处理的更新内 (executionContext & LegacyUnbatchedContext) !== NoContext && // Check if we're not already rendering // 检查是否尚未渲染 (executionContext & (RenderContext | CommitContext)) === NoContext ) { // Register pending interactions on the root to avoid losing traced interaction data. // 在根上注册待处理的交互,以避免丢失跟踪的交互数据。 schedulePendingInteractions(root, expirationTime); // This is a legacy edge case. The initial mount of a ReactDOM.render-ed // root inside of batchedUpdates should be synchronous, but layout updates // should be deferred until the end of the batch. performSyncWorkOnRoot(root); } else { ensureRootIsScheduled(root); schedulePendingInteractions(root, expirationTime); if (executionContext === NoContext) { // Flush the synchronous work now, unless we're already working or inside // a batch. This is intentionally inside scheduleUpdateOnFiber instead of // scheduleCallbackForFiber to preserve the ability to schedule a callback // without immediately flushing it. We only do this for user-initiated // updates, to preserve historical behavior of legacy mode. // 推入调度任务队列 flushSyncCallbackQueue(); } } } else { // Schedule a discrete update but only if it's not Sync. if ( (executionContext & DiscreteEventContext) !== NoContext && // Only updates at user-blocking priority or greater are considered // discrete, even inside a discrete event. (priorityLevel === UserBlockingPriority || priorityLevel === ImmediatePriority) ) { // This is the result of a discrete event. Track the lowest priority // discrete update per root so we can flush them early, if needed. if (rootsWithPendingDiscreteUpdates === null) { rootsWithPendingDiscreteUpdates = new Map([[root, expirationTime]]); } else { const lastDiscreteTime = rootsWithPendingDiscreteUpdates.get(root); if ( lastDiscreteTime === undefined || lastDiscreteTime > expirationTime ) { rootsWithPendingDiscreteUpdates.set(root, expirationTime); } } } // Schedule other updates after in case the callback is sync. ensureRootIsScheduled(root); schedulePendingInteractions(root, expirationTime); } }上面源码主要做了以下几件事
调用 markUpdateTimeFromFiberToRoot 更新 Fiber 节点的 expirationTime
ensureRootIsScheduled(更新重点)
schedulePendingInteractions 实际上会调用 scheduleInteractions