当前位置: 首页 > ops >正文

React源码6 三大核心模块之一:commit, finishConcurrentRender函数

commit阶段流程图

一、finishConcurrentRender函数

function finishConcurrentRender(root, exitStatus, lanes) {switch (exitStatus) {case RootInProgress:case RootFatalErrored:{throw new Error('Root did not complete. This is a bug in React.');}// Flow knows about invariant, so it complains if I add a break// statement, but eslint doesn't know about invariant, so it complains// if I do. eslint-disable-next-line no-fallthroughcase RootErrored:{// We should have already attempted to retry this tree. If we reached// this point, it errored again. Commit it.commitRoot(root, workInProgressRootRecoverableErrors, workInProgressTransitions);break;}case RootSuspended:{markRootSuspended$1(root, lanes); // We have an acceptable loading state. We need to figure out if we// should immediately commit it or wait a bit.if (includesOnlyRetries(lanes) && // do not delay if we're inside an act() scope!shouldForceFlushFallbacksInDEV()) {// This render only included retries, no updates. Throttle committing// retries so that we don't show too many loading states too quickly.var msUntilTimeout = globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now(); // Don't bother with a very short suspense time.if (msUntilTimeout > 10) {var nextLanes = getNextLanes(root, NoLanes);if (nextLanes !== NoLanes) {// There's additional work on this root.break;}var suspendedLanes = root.suspendedLanes;if (!isSubsetOfLanes(suspendedLanes, lanes)) {// We should prefer to render the fallback of at the last// suspended level. Ping the last suspended level to try// rendering it again.// FIXME: What if the suspended lanes are Idle? Should not restart.var eventTime = requestEventTime();markRootPinged(root, suspendedLanes);break;} // The render is suspended, it hasn't timed out, and there's no// lower priority work to do. Instead of committing the fallback// immediately, wait for more data to arrive.root.timeoutHandle = scheduleTimeout(commitRoot.bind(null, root, workInProgressRootRecoverableErrors, workInProgressTransitions), msUntilTimeout);break;}} // The work expired. Commit immediately.commitRoot(root, workInProgressRootRecoverableErrors, workInProgressTransitions);break;}case RootSuspendedWithDelay:{markRootSuspended$1(root, lanes);if (includesOnlyTransitions(lanes)) {// This is a transition, so we should exit without committing a// placeholder and without scheduling a timeout. Delay indefinitely// until we receive more data.break;}if (!shouldForceFlushFallbacksInDEV()) {// This is not a transition, but we did trigger an avoided state.// Schedule a placeholder to display after a short delay, using the Just// Noticeable Difference.// TODO: Is the JND optimization worth the added complexity? If this is// the only reason we track the event time, then probably not.// Consider removing.var mostRecentEventTime = getMostRecentEventTime(root, lanes);var eventTimeMs = mostRecentEventTime;var timeElapsedMs = now() - eventTimeMs;var _msUntilTimeout = jnd(timeElapsedMs) - timeElapsedMs; // Don't bother with a very short suspense time.if (_msUntilTimeout > 10) {// Instead of committing the fallback immediately, wait for more data// to arrive.root.timeoutHandle = scheduleTimeout(commitRoot.bind(null, root, workInProgressRootRecoverableErrors, workInProgressTransitions), _msUntilTimeout);break;}} // Commit the placeholder.commitRoot(root, workInProgressRootRecoverableErrors, workInProgressTransitions);break;}case RootCompleted:{// The work completed. Ready to commit.commitRoot(root, workInProgressRootRecoverableErrors, workInProgressTransitions);break;}default:{throw new Error('Unknown root exit status.');}}
}

二、commitRoot函数

function commitRoot(root, recoverableErrors, transitions) {// TODO: This no longer makes any sense. We already wrap the mutation and// layout phases. Should be able to remove.var previousUpdateLanePriority = getCurrentUpdatePriority();var prevTransition = ReactCurrentBatchConfig$3.transition;try {ReactCurrentBatchConfig$3.transition = null;setCurrentUpdatePriority(DiscreteEventPriority);commitRootImpl(root, recoverableErrors, transitions, previousUpdateLanePriority);} finally {ReactCurrentBatchConfig$3.transition = prevTransition;setCurrentUpdatePriority(previousUpdateLanePriority);}return null;
}

三、commitRootImpl函数

function commitRootImpl(root, recoverableErrors, transitions, renderPriorityLevel) {do {// `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which// means `flushPassiveEffects` will sometimes result in additional// passive effects. So we need to keep flushing in a loop until there are// no more pending effects.// TODO: Might be better if `flushPassiveEffects` did not automatically// flush synchronous work at the end, to avoid factoring hazards like this.flushPassiveEffects();} while (rootWithPendingPassiveEffects !== null);flushRenderPhaseStrictModeWarningsInDEV();if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {throw new Error('Should not already be working.');}var finishedWork = root.finishedWork;var lanes = root.finishedLanes;{markCommitStarted(lanes);}if (finishedWork === null) {{markCommitStopped();}return null;} else {{if (lanes === NoLanes) {error('root.finishedLanes should not be empty during a commit. This is a ' + 'bug in React.');}}}root.finishedWork = null;root.finishedLanes = NoLanes;if (finishedWork === root.current) {throw new Error('Cannot commit the same tree as before. This error is likely caused by ' + 'a bug in React. Please file an issue.');} // commitRoot never returns a continuation; it always finishes synchronously.// So we can clear these now to allow a new callback to be scheduled.root.callbackNode = null;root.callbackPriority = NoLane; // Update the first and last pending times on this root. The new first// pending time is whatever is left on the root fiber.var remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);markRootFinished(root, remainingLanes);if (root === workInProgressRoot) {// We can reset these now that they are finished.workInProgressRoot = null;workInProgress = null;workInProgressRootRenderLanes = NoLanes;} // If there are pending passive effects, schedule a callback to process them.// Do this as early as possible, so it is queued before anything else that// might get scheduled in the commit phase. (See #16714.)// TODO: Delete all other places that schedule the passive effect callback// They're redundant.if ((finishedWork.subtreeFlags & PassiveMask) !== NoFlags || (finishedWork.flags & PassiveMask) !== NoFlags) {if (!rootDoesHavePassiveEffects) {rootDoesHavePassiveEffects = true;// to store it in pendingPassiveTransitions until they get processed// We need to pass this through as an argument to commitRoot// because workInProgressTransitions might have changed between// the previous render and commit if we throttle the commit// with setTimeoutpendingPassiveTransitions = transitions;scheduleCallback$1(NormalPriority, function () {flushPassiveEffects(); // This render triggered passive effects: release the root cache pool// *after* passive effects fire to avoid freeing a cache pool that may// be referenced by a node in the tree (HostRoot, Cache boundary etc)return null;});}} // Check if there are any effects in the whole tree.// TODO: This is left over from the effect list implementation, where we had// to check for the existence of `firstEffect` to satisfy Flow. I think the// only other reason this optimization exists is because it affects profiling.// Reconsider whether this is necessary.var subtreeHasEffects = (finishedWork.subtreeFlags & (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !== NoFlags;var rootHasEffect = (finishedWork.flags & (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !== NoFlags;if (subtreeHasEffects || rootHasEffect) {var prevTransition = ReactCurrentBatchConfig$3.transition;ReactCurrentBatchConfig$3.transition = null;var previousPriority = getCurrentUpdatePriority();setCurrentUpdatePriority(DiscreteEventPriority);var prevExecutionContext = executionContext;executionContext |= CommitContext; // Reset this to null before calling lifecyclesReactCurrentOwner$2.current = null; // The commit phase is broken into several sub-phases. We do a separate pass// of the effect list for each phase: all mutation effects come before all// layout effects, and so on.// The first phase a "before mutation" phase. We use this phase to read the// state of the host tree right before we mutate it. This is where// getSnapshotBeforeUpdate is called.var shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects(root, finishedWork);{// Mark the current commit time to be shared by all Profilers in this// batch. This enables them to be grouped later.recordCommitTime();}commitMutationEffects(root, finishedWork, lanes);resetAfterCommit(root.containerInfo); // The work-in-progress tree is now the current tree. This must come after// the mutation phase, so that the previous tree is still current during// componentWillUnmount, but before the layout phase, so that the finished// work is current during componentDidMount/Update.root.current = finishedWork; // The next phase is the layout phase, where we call effects that read{markLayoutEffectsStarted(lanes);}commitLayoutEffects(finishedWork, root, lanes);{markLayoutEffectsStopped();}// opportunity to paint.requestPaint();executionContext = prevExecutionContext; // Reset the priority to the previous non-sync value.setCurrentUpdatePriority(previousPriority);ReactCurrentBatchConfig$3.transition = prevTransition;} else {// No effects.root.current = finishedWork; // Measure these anyway so the flamegraph explicitly shows that there were// no effects.// TODO: Maybe there's a better way to report this.{recordCommitTime();}}var rootDidHavePassiveEffects = rootDoesHavePassiveEffects;if (rootDoesHavePassiveEffects) {// This commit has passive effects. Stash a reference to them. But don't// schedule a callback until after flushing layout work.rootDoesHavePassiveEffects = false;rootWithPendingPassiveEffects = root;pendingPassiveEffectsLanes = lanes;} else {{nestedPassiveUpdateCount = 0;rootWithPassiveNestedUpdates = null;}} // Read this again, since an effect might have updated itremainingLanes = root.pendingLanes; // Check if there's remaining work on this root// TODO: This is part of the `componentDidCatch` implementation. Its purpose// is to detect whether something might have called setState inside// `componentDidCatch`. The mechanism is known to be flawed because `setState`// inside `componentDidCatch` is itself flawed — that's why we recommend// `getDerivedStateFromError` instead. However, it could be improved by// checking if remainingLanes includes Sync work, instead of whether there's// any work remaining at all (which would also include stuff like Suspense// retries or transitions). It's been like this for a while, though, so fixing// it probably isn't that urgent.if (remainingLanes === NoLanes) {// If there's no remaining work, we can clear the set of already failed// error boundaries.legacyErrorBoundariesThatAlreadyFailed = null;}{if (!rootDidHavePassiveEffects) {commitDoubleInvokeEffectsInDEV(root.current, false);}}onCommitRoot(finishedWork.stateNode, renderPriorityLevel);{if (isDevToolsPresent) {root.memoizedUpdaters.clear();}}{onCommitRoot$1();} // Always call this before exiting `commitRoot`, to ensure that any// additional work on this root is scheduled.ensureRootIsScheduled(root, now());if (recoverableErrors !== null) {// There were errors during this render, but recovered from them without// needing to surface it to the UI. We log them here.var onRecoverableError = root.onRecoverableError;for (var i = 0; i < recoverableErrors.length; i++) {var recoverableError = recoverableErrors[i];var componentStack = recoverableError.stack;var digest = recoverableError.digest;onRecoverableError(recoverableError.value, {componentStack: componentStack,digest: digest});}}if (hasUncaughtError) {hasUncaughtError = false;var error$1 = firstUncaughtError;firstUncaughtError = null;throw error$1;} // If the passive effects are the result of a discrete render, flush them// synchronously at the end of the current task so that the result is// immediately observable. Otherwise, we assume that they are not// order-dependent and do not need to be observed by external systems, so we// can wait until after paint.// TODO: We can optimize this by not scheduling the callback earlier. Since we// currently schedule the callback in multiple places, will wait until those// are consolidated.if (includesSomeLane(pendingPassiveEffectsLanes, SyncLane) && root.tag !== LegacyRoot) {flushPassiveEffects();} // Read this again, since a passive effect might have updated itremainingLanes = root.pendingLanes;if (includesSomeLane(remainingLanes, SyncLane)) {{markNestedUpdateScheduled();} // Count the number of times the root synchronously re-renders without// finishing. If there are too many, it indicates an infinite update loop.if (root === rootWithNestedUpdates) {nestedUpdateCount++;} else {nestedUpdateCount = 0;rootWithNestedUpdates = root;}} else {nestedUpdateCount = 0;} // If layout work was scheduled, flush it now.flushSyncCallbacks();{markCommitStopped();}return null;
}

http://www.xdnf.cn/news/15430.html

相关文章:

  • 24.找到列表中最大或最小值的索引
  • Pitaya 是一个简单、快速、轻量级的游戏服务器框架,它为分布式多人游戏和服务器端应用程序提供了一个基本的开发框架
  • 优雅的Java:01.数据更新如何更优雅
  • Python学习之路(十二)-开发和优化处理大数据量接口
  • 从springcloud-gateway了解同步和异步,webflux webMvc、共享变量
  • S7-200 SMART PLC:不同CPU及数字量 IO 接线全解析
  • 构建强大的物联网架构所需了解的一切
  • Janitor AI重塑人机交互的沉浸式智能体验
  • 大型语言模型(LLM)的技术面试题
  • 【机器人】REGNav 具身导航 | 跨房间引导 | 图像目标导航 AAAI 2025
  • 【算法-BFS 解决最短路问题】探索BFS在图论中的应用:最短路径问题的高效解法
  • docker停止所有容器和删除所有镜像
  • 【Docker基础】Dockerfile指令速览:高级构建指令详解
  • 闲庭信步使用图像验证平台加速FPGA的开发:第十六课——图像五行缓存的FPGA实现
  • 常见的 Commit 描述 分类及示例
  • 2025-07-15通过边缘线检测图像里的主体有没有出血
  • 2025-07-15 李沐深度学习6——Softmax回归
  • 实测两款效率工具:驾考刷题和证件照处理的免费方案
  • vscode里面怎么配置ssh步骤
  • 算法学习笔记:22.贪心算法之霍夫曼编码 ——从原理到实战,涵盖 LeetCode 与考研 408 例题
  • JavaScript进阶篇——第三章 箭头函数核心
  • 17. 什么是 webSocket ?
  • 面试遇到的问题
  • 项目总体框架(servlet+axios+Mybatis)
  • Qt图形视图框架5-状态机框架
  • 【Python进阶】深度复制——deepcopy
  • 【人工智能】通过 Dify 构建智能助手
  • JavaScript书写基础和基本数据类型
  • 8:从USB摄像头把声音拿出来--ALSA大佬登场!
  • 算法训练营day18 530.二叉搜索树的最小绝对差、501.二叉搜索树中的众数、236. 二叉树的最近公共祖先