functionworkLoopConcurrent() { // Perform work until Scheduler asks us to yield while (workInProgress !== null && !shouldYield()) { // $FlowFixMe[incompatible-call] found when upgrading Flow performUnitOfWork(workInProgress); } }
// Determine the next lanes to work on, and their priority. const workInProgressRoot = getWorkInProgressRoot(); const workInProgressRootRenderLanes = getWorkInProgressRootRenderLanes();
const existingCallbackNode = root.callbackNode; if ( // 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. // // 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)) { // sy- setState // sy-no 初次渲染 // 同步工作始终在微任务结束时刷新,因此我们不需要安排额外的任务。 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. !( __DEV__ && ReactCurrentActQueue.current !== 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)) { case DiscreteEventPriority: schedulerPriorityLevel = ImmediateSchedulerPriority; break; case ContinuousEventPriority: schedulerPriorityLevel = UserBlockingSchedulerPriority; break; case DefaultEventPriority: // 32 // ? sy 页面初次渲染、transition(128) schedulerPriorityLevel = NormalSchedulerPriority; // 3 break; case IdleEventPriority: schedulerPriorityLevel = IdleSchedulerPriority; break; default: schedulerPriorityLevel = NormalSchedulerPriority; break; }
// Flush any pending passive effects before deciding which lanes to work on, // in case they schedule additional work. const originalCallbackNode = root.callbackNode; // ...
if (exitStatus !== RootInProgress) { let renderWasConcurrent = shouldTimeSlice; do { if (exitStatus === RootDidNotComplete) { // The render unwound without completing the tree. This happens in special // cases where need to exit the current render without producing a // consistent tree or committing. markRootSuspended(root, lanes, NoLane); } else { // ! 2. render结束,做一些检查
exportfunctiongetContinuationForRoot( root: FiberRoot, originalCallbackNode: mixed, ): RenderTaskFn | null{ // This is called at the end of `performConcurrentWorkOnRoot` to determine // if we need to schedule a continuation task. // // 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(root, now()); if (root.callbackNode === originalCallbackNode) { // The task node scheduled for this root is the same one that's // currently executed. Need to return a continuation. return performConcurrentWorkOnRoot.bind(null, root); } returnnull; }
const callback = currentTask.callback; if (typeof callback === 'function') { // ... const continuationCallback = callback(didUserCallbackTimeout); // 返回值continuationCallback是函数,也就是performConcurrentWorkOnRoot执行后的返回值performConcurrentWorkOnRoot.bind(null, root)或null,如果是函数就继续,不是的话,如果判断如果currentTask === peek(taskQueue),currentTask 出队 if (typeof continuationCallback === 'function') { // If a continuation is returned, immediately yield to the main thread // regardless of how much time is left in the current time slice. // $FlowFixMe[incompatible-use] found when upgrading Flow currentTask.callback = continuationCallback; if (enableProfiling) { // $FlowFixMe[incompatible-call] found when upgrading Flow markTaskYield(currentTask, currentTime); } advanceTimers(currentTime); returntrue; } else { if (enableProfiling) { // $FlowFixMe[incompatible-call] found when upgrading Flow markTaskCompleted(currentTask, currentTime); // $FlowFixMe[incompatible-use] found when upgrading Flow currentTask.isQueued = false; } if (currentTask === peek(taskQueue)) { pop(taskQueue); } advanceTimers(currentTime); } } else { pop(taskQueue); } currentTask = peek(taskQueue);
// 在 调度更新的过程中会被调用 // 检查是有lanes挨饿,如果有,则标记他们过期,即提升优先级(以便下次执行)。 exportfunctionmarkStarvedLanesAsExpired( root: FiberRoot, currentTime: number, ): void{ // TODO: This gets called every time we yield. We can optimize by storing // the earliest expiration time on the root. Then use that to quickly bail out // of this function.
// Iterate through the pending lanes and check if we've reached their // expiration time. If so, we'll assume the update is being starved and mark // it as expired to force it to finish. // 遍历pending lanes,并检查是否已经达到它们的过期时间。 // 如果是,我们就认为这个update挨饿了,并将其标记为已过期,以强制其完成。 // TODO: We should be able to replace this with upgradePendingLanesToSync // // We exclude retry lanes because those must always be time sliced, in order // to unwrap uncached promises. // TODO: Write a test for this let lanes = enableRetryLaneExpiration ? pendingLanes // ? sy : pendingLanes & ~RetryLanes;
while (lanes > 0) { // 下面两行代码的作用是找到lanes中最低位的1,即优先级最 const index = pickArbitraryLaneIndex(lanes); // 把1左移index位,即得到一个只有第index位为1的子掩码 const lane = 1 << index;
const expirationTime = expirationTimes[index]; if (expirationTime === NoTimestamp) { // sy- console.log('%c [ ]-1469', 'font-size:13px; background:pink; color:#bf2c9f;', lane) // Found a pending lane with no expiration time. If it's not suspended, or // if it's pinged, assume it's CPU-bound. Compute a new expiration time // using the current time. // 如果这个 pending lane 没有过期时间 // 如果它没有被挂起且需要更新,我们就认为它是CPU密集型操作。 // 用当前时间计算出一个新的过期时间给它。 // CPU bound / IO Bound if ( (lane & suspendedLanes) === NoLanes || (lane & pingedLanes) !== NoLanes ) { // sy- console.log('%c [ 饿死 ]-482', 'font-size:13px; background:pink; color:#bf2c9f;', lane) // Assumes timestamps are monotonically increasing. // 假设timestamps(时间戳)是单调递增的 expirationTimes[index] = computeExpirationTime(lane, currentTime); } } elseif (expirationTime <= currentTime) { // 这个 pending lane 已经过期了 // This lane expired root.expiredLanes |= lane; } // 把lane从lanes中移除,计算下一个lane lanes &= ~lane; } }
// 检查root是否包含过期的lane exportfunctionincludesExpiredLane(root: FiberRoot, lanes: Lanes): boolean{ // This is a separate check from includesBlockingLane because a lane can // expire after a render has already started. return (lanes & root.expiredLanes) !== NoLanes; }