Best JavaScript code snippet using playwright-internal
ReactFiberWorkLoop.new.js
Source:ReactFiberWorkLoop.new.js
...670 }671 // å»æ§è¡æ´æ°ä»»å¡çå·¥ä½å¾ªç¯ï¼ä¸æ¦è¶
åºæ¶é´çï¼åä¼éåºrenderRootConcurrent672 // å»æ§è¡ä¸é¢çé»è¾673 // 3. æé fiberæ 674 let exitStatus = renderRootConcurrent(root, lanes);675 if (676 includesSomeLane(677 workInProgressRootIncludedLanes,678 workInProgressRootUpdatedLanes,679 )680 ) {681 // The render included lanes that were updated during the render phase.682 // For example, when unhiding a hidden tree, we include all the lanes683 // that were previously skipped when the tree was hidden. That set of684 // lanes is a superset of the lanes we started rendering with.685 //686 // So we'll throw out the current work and restart.687 // å¦æå¨renderè¿ç¨ä¸äº§çäºæ°çupdate, ä¸æ°updateçä¼å
级ä¸æårenderçä¼å
级æ交é688 // é£ä¹æårenderæ æ, 丢å¼æårenderçç»æ, çå¾
ä¸ä¸æ¬¡è°åº¦689 prepareFreshStack(root, NoLanes);690 } else if (exitStatus !== RootIncomplete) {691 // 4. å¼å¸¸å¤ç: æå¯è½fiberæé è¿ç¨ä¸åºç°å¼å¸¸692 if (exitStatus === RootErrored) {693 executionContext |= RetryAfterError;694 // If an error occurred during hydration,695 // discard server response and fall back to client side render.696 if (root.hydrate) {697 root.hydrate = false;698 clearContainer(root.containerInfo);699 }700 // If something threw an error, try rendering one more time. We'll render701 // synchronously to block concurrent data mutations, and we'll includes702 // all pending updates are included. If it still fails after the second703 // attempt, we'll give up and commit the resulting tree.704 lanes = getLanesToRetrySynchronouslyOnError(root);705 if (lanes !== NoLanes) {706 exitStatus = renderRootSync(root, lanes);707 }708 }709 if (exitStatus === RootFatalErrored) {710 const fatalError = workInProgressRootFatalError;711 prepareFreshStack(root, NoLanes);712 markRootSuspended(root, lanes);713 ensureRootIsScheduled(root, now());714 throw fatalError;715 }716 // We now have a consistent tree. The next step is either to commit it,717 // or, if something suspended, wait to commit it after a timeout.718 const finishedWork: Fiber = (root.current.alternate: any);719 root.finishedWork = finishedWork;720 root.finishedLanes = lanes;721 // 5. è¾åº: 渲æfiberæ 722 finishConcurrentRender(root, exitStatus, lanes);723 }724 // è°ç¨ensureRootIsScheduledå»æ£æ¥ææ è¿æä»»å¡ï¼æ¯å¦éè¦è°åº¦è¿æä»»å¡725 ensureRootIsScheduled(root, now());726 // æ´æ°ä»»å¡æªå®æï¼returnèªå·±ï¼æ¹ä¾¿Schedulerå¤æä»»å¡å®æç¶æ727 if (root.callbackNode === originalCallbackNode) {728 // The task node scheduled for this root is the same one that's729 // currently executed. Need to return a continuation.730 return performConcurrentWorkOnRoot.bind(null, root);731 }732 enableLog && console.log('performConcurrentWorkOnRoot end')733 // å¦åretutn nullï¼è¡¨ç¤ºä»»å¡å·²ç»å®æï¼éç¥Scheduleråæ¢è°åº¦734 return null;735}736function finishConcurrentRender(root, exitStatus, lanes) {737 switch (exitStatus) {738 case RootIncomplete:739 case RootFatalErrored: {740 invariant(false, 'Root did not complete. This is a bug in React.');741 }742 // Flow knows about invariant, so it complains if I add a break743 // statement, but eslint doesn't know about invariant, so it complains744 // if I do. eslint-disable-next-line no-fallthrough745 case RootErrored: {746 // We should have already attempted to retry this tree. If we reached747 // this point, it errored again. Commit it.748 commitRoot(root);749 break;750 }751 case RootSuspended: {752 markRootSuspended(root, lanes);753 // We have an acceptable loading state. We need to figure out if we754 // should immediately commit it or wait a bit.755 if (756 includesOnlyRetries(lanes)757 ) {758 // This render only included retries, no updates. Throttle committing759 // retries so that we don't show too many loading states too quickly.760 const msUntilTimeout =761 globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now();762 // Don't bother with a very short suspense time.763 if (msUntilTimeout > 10) {764 const nextLanes = getNextLanes(root, NoLanes);765 if (nextLanes !== NoLanes) {766 // There's additional work on this root.767 break;768 }769 const suspendedLanes = root.suspendedLanes;770 if (!isSubsetOfLanes(suspendedLanes, lanes)) {771 // We should prefer to render the fallback of at the last772 // suspended level. Ping the last suspended level to try773 // rendering it again.774 // FIXME: What if the suspended lanes are Idle? Should not restart.775 const eventTime = requestEventTime();776 markRootPinged(root, suspendedLanes, eventTime);777 break;778 }779 // The render is suspended, it hasn't timed out, and there's no780 // lower priority work to do. Instead of committing the fallback781 // immediately, wait for more data to arrive.782 root.timeoutHandle = scheduleTimeout(783 commitRoot.bind(null, root),784 msUntilTimeout,785 );786 break;787 }788 }789 // The work expired. Commit immediately.790 commitRoot(root);791 break;792 }793 case RootSuspendedWithDelay: {794 markRootSuspended(root, lanes);795 if (includesOnlyTransitions(lanes)) {796 // This is a transition, so we should exit without committing a797 // placeholder and without scheduling a timeout. Delay indefinitely798 // until we receive more data.799 break;800 }801 // This is not a transition, but we did trigger an avoided state.802 // Schedule a placeholder to display after a short delay, using the Just803 // Noticeable Difference.804 // TODO: Is the JND optimization worth the added complexity? If this is805 // the only reason we track the event time, then probably not.806 // Consider removing.807 const mostRecentEventTime = getMostRecentEventTime(root, lanes);808 const eventTimeMs = mostRecentEventTime;809 const timeElapsedMs = now() - eventTimeMs;810 const msUntilTimeout = jnd(timeElapsedMs) - timeElapsedMs;811 // Don't bother with a very short suspense time.812 if (msUntilTimeout > 10) {813 // Instead of committing the fallback immediately, wait for more data814 // to arrive.815 root.timeoutHandle = scheduleTimeout(816 commitRoot.bind(null, root),817 msUntilTimeout,818 );819 break;820 }821 // Commit the placeholder.822 commitRoot(root);823 break;824 }825 case RootCompleted: {826 // The work completed. Ready to commit.827 commitRoot(root);828 break;829 }830 default: {831 invariant(false, 'Unknown root exit status.');832 }833 }834}835function markRootSuspended(root, suspendedLanes) {836 // When suspending, we should always exclude lanes that were pinged or (more837 // rarely, since we try to avoid it) updated during the render phase.838 // TODO: Lol maybe there's a better way to factor this besides this839 // obnoxiously named function :)840 suspendedLanes = removeLanes(suspendedLanes, workInProgressRootPingedLanes);841 suspendedLanes = removeLanes(suspendedLanes, workInProgressRootUpdatedLanes);842 markRootSuspended_dontCallThisOneDirectly(root, suspendedLanes);843}844// This is the entry point for synchronous tasks that don't go845// through Scheduler846function performSyncWorkOnRoot(root) {847 invariant(848 (executionContext & (RenderContext | CommitContext)) === NoContext,849 'Should not already be working.',850 );851 852 console.log('ReactFiberWorkLoop.new: performSyncWorkOnRoot')853 if (!__LOG_NAMES__.length || __LOG_NAMES__.includes('performSyncWorkOnRoot')) debugger854 flushPassiveEffects();855 let lanes;856 let exitStatus;857 if (858 root === workInProgressRoot &&859 includesSomeLane(root.expiredLanes, workInProgressRootRenderLanes)860 ) {861 // There's a partial tree, and at least one of its lanes has expired. Finish862 // rendering it before rendering the rest of the expired work.863 lanes = workInProgressRootRenderLanes;864 exitStatus = renderRootSync(root, lanes);865 if (866 includesSomeLane(867 workInProgressRootIncludedLanes,868 workInProgressRootUpdatedLanes,869 )870 ) {871 // The render included lanes that were updated during the render phase.872 // For example, when unhiding a hidden tree, we include all the lanes873 // that were previously skipped when the tree was hidden. That set of874 // lanes is a superset of the lanes we started rendering with.875 //876 // Note that this only happens when part of the tree is rendered877 // concurrently. If the whole tree is rendered synchronously, then there878 // are no interleaved events.879 lanes = getNextLanes(root, lanes);880 exitStatus = renderRootSync(root, lanes);881 }882 } else {883 lanes = getNextLanes(root, NoLanes);884 exitStatus = renderRootSync(root, lanes);885 }886 if (root.tag !== LegacyRoot && exitStatus === RootErrored) {887 executionContext |= RetryAfterError;888 // If an error occurred during hydration,889 // discard server response and fall back to client side render.890 if (root.hydrate) {891 root.hydrate = false;892 clearContainer(root.containerInfo);893 }894 // If something threw an error, try rendering one more time. We'll render895 // synchronously to block concurrent data mutations, and we'll includes896 // all pending updates are included. If it still fails after the second897 // attempt, we'll give up and commit the resulting tree.898 lanes = getLanesToRetrySynchronouslyOnError(root);899 if (lanes !== NoLanes) {900 exitStatus = renderRootSync(root, lanes);901 }902 }903 if (exitStatus === RootFatalErrored) {904 const fatalError = workInProgressRootFatalError;905 prepareFreshStack(root, NoLanes);906 markRootSuspended(root, lanes);907 ensureRootIsScheduled(root, now());908 throw fatalError;909 }910 // We now have a consistent tree. Because this is a sync render, we911 // will commit it even if something suspended.912 const finishedWork: Fiber = (root.current.alternate: any);913 root.finishedWork = finishedWork;914 root.finishedLanes = lanes;915 commitRoot(root);916 // Before exiting, make sure there's a callback scheduled for the next917 // pending level.918 ensureRootIsScheduled(root, now());919 return null;920}921export function flushRoot(root: FiberRoot, lanes: Lanes) {922 markRootExpired(root, lanes);923 ensureRootIsScheduled(root, now());924 if ((executionContext & (RenderContext | CommitContext)) === NoContext) {925 resetRenderTimer();926 flushSyncCallbackQueue();927 }928}929export function getExecutionContext(): ExecutionContext {930 return executionContext;931}932export function flushDiscreteUpdates() {933 // TODO: Should be able to flush inside batchedUpdates, but not inside `act`.934 // However, `act` uses `batchedUpdates`, so there's no way to distinguish935 // those two cases. Need to fix this before exposing flushDiscreteUpdates936 // as a public API.937 if (938 (executionContext & (BatchedContext | RenderContext | CommitContext)) !==939 NoContext940 ) {941 // We're already rendering, so we can't synchronously flush pending work.942 // This is probably a nested event dispatch triggered by a lifecycle/effect,943 // like `el.focus()`. Exit.944 return;945 }946 flushPendingDiscreteUpdates();947 // If the discrete updates scheduled passive effects, flush them now so that948 // they fire before the next serial event.949 flushPassiveEffects();950}951export function deferredUpdates<A>(fn: () => A): A {952 return runWithPriority(NormalSchedulerPriority, fn);953}954function flushPendingDiscreteUpdates() {955 if (rootsWithPendingDiscreteUpdates !== null) {956 // For each root with pending discrete updates, schedule a callback to957 // immediately flush them.958 const roots = rootsWithPendingDiscreteUpdates;959 rootsWithPendingDiscreteUpdates = null;960 roots.forEach(root => {961 markDiscreteUpdatesExpired(root);962 ensureRootIsScheduled(root, now());963 });964 }965 // Now flush the immediate queue.966 flushSyncCallbackQueue();967}968export function batchedUpdates<A, R>(fn: A => R, a: A): R {969 const prevExecutionContext = executionContext;970 executionContext |= BatchedContext;971 try {972 return fn(a);973 } finally {974 executionContext = prevExecutionContext;975 if (executionContext === NoContext) {976 // Flush the immediate callbacks that were scheduled during this batch977 resetRenderTimer();978 flushSyncCallbackQueue();979 }980 }981}982export function batchedEventUpdates<A, R>(fn: A => R, a: A): R {983 const prevExecutionContext = executionContext;984 // ææçäºä»¶å¨è§¦åçæ¶åï¼é½ä¼å
è°ç¨ batchedEventUpdates è¿ä¸ªæ¹æ³985 // å¨è¿éå°±ä¼ä¿®æ¹ executionContext çå¼ï¼React å°±ç¥éæ¤æ¶ç setState å¨èªå·±çææ§ä¸986 executionContext |= EventContext;987 try {988 return fn(a);989 } finally {990 executionContext = prevExecutionContext;991 // è°ç¨ç»æåï¼è°ç¨ flushSyncCallbackQueue992 if (executionContext === NoContext) {993 // Flush the immediate callbacks that were scheduled during this batch994 resetRenderTimer();995 flushSyncCallbackQueue();996 }997 }998}999export function discreteUpdates<A, B, C, D, R>(1000 fn: (A, B, C) => R,1001 a: A,1002 b: B,1003 c: C,1004 d: D,1005): R {1006 const prevExecutionContext = executionContext;1007 executionContext |= DiscreteEventContext;1008 try {1009 return runWithPriority(1010 UserBlockingSchedulerPriority,1011 fn.bind(null, a, b, c, d),1012 );1013 } finally {1014 executionContext = prevExecutionContext;1015 if (executionContext === NoContext) {1016 // Flush the immediate callbacks that were scheduled during this batch1017 resetRenderTimer();1018 flushSyncCallbackQueue();1019 }1020 }1021}1022export function unbatchedUpdates<A, R>(fn: (a: A) => R, a: A): R {1023 const prevExecutionContext = executionContext;1024 executionContext &= ~BatchedContext;1025 executionContext |= LegacyUnbatchedContext;1026 try {1027 return fn(a);1028 } finally {1029 executionContext = prevExecutionContext;1030 if (executionContext === NoContext) {1031 // Flush the immediate callbacks that were scheduled during this batch1032 resetRenderTimer();1033 flushSyncCallbackQueue();1034 }1035 }1036}1037export function flushSync<A, R>(fn: A => R, a: A): R {1038 const prevExecutionContext = executionContext;1039 if ((prevExecutionContext & (RenderContext | CommitContext)) !== NoContext) {1040 return fn(a);1041 }1042 executionContext |= BatchedContext;1043 try {1044 if (fn) {1045 return runWithPriority(ImmediateSchedulerPriority, fn.bind(null, a));1046 } else {1047 return (undefined: $FlowFixMe);1048 }1049 } finally {1050 executionContext = prevExecutionContext;1051 // Flush the immediate callbacks that were scheduled during this batch.1052 // Note that this will happen even if batchedUpdates is higher up1053 // the stack.1054 flushSyncCallbackQueue();1055 }1056}1057export function flushControlled(fn: () => mixed): void {1058 const prevExecutionContext = executionContext;1059 executionContext |= BatchedContext;1060 try {1061 runWithPriority(ImmediateSchedulerPriority, fn);1062 } finally {1063 executionContext = prevExecutionContext;1064 if (executionContext === NoContext) {1065 // Flush the immediate callbacks that were scheduled during this batch1066 resetRenderTimer();1067 flushSyncCallbackQueue();1068 }1069 }1070}1071export function pushRenderLanes(fiber: Fiber, lanes: Lanes) {1072 pushToStack(subtreeRenderLanesCursor, subtreeRenderLanes, fiber);1073 subtreeRenderLanes = mergeLanes(subtreeRenderLanes, lanes);1074 workInProgressRootIncludedLanes = mergeLanes(1075 workInProgressRootIncludedLanes,1076 lanes,1077 );1078}1079export function popRenderLanes(fiber: Fiber) {1080 subtreeRenderLanes = subtreeRenderLanesCursor.current;1081 popFromStack(subtreeRenderLanesCursor, fiber);1082}1083/**1084å·æ°æ 帧: éç½® FiberRootä¸çå
¨å±å±æ§ å `fiberæ æé `循ç¯è¿ç¨ä¸çå
¨å±åé1085*/1086function prepareFreshStack(root: FiberRoot, lanes: Lanes) {1087 // workInProgressRoot第ä¸æ¬¡å¨è¿éåå§å1088 enableLog && console.log('prepareFreshStack start')1089 if (!__LOG_NAMES__.length || __LOG_NAMES__.includes('prepareFreshStack')) debugger1090 1091 root.finishedWork = null;1092 root.finishedLanes = NoLanes;1093 const timeoutHandle = root.timeoutHandle;1094 if (timeoutHandle !== noTimeout) { // noTimeout === -11095 // The root previous suspended and scheduled a timeout to commit a fallback1096 // state. Now that we have additional work, cancel the timeout.1097 root.timeoutHandle = noTimeout;1098 // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above1099 cancelTimeout(timeoutHandle);1100 }1101 if (workInProgress !== null) {1102 let interruptedWork = workInProgress.return;1103 while (interruptedWork !== null) {1104 unwindInterruptedWork(interruptedWork);1105 interruptedWork = interruptedWork.return;1106 }1107 }1108 workInProgressRoot = root;1109 workInProgress = createWorkInProgress(root.current, null);1110 workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;1111 workInProgressRootExitStatus = RootIncomplete;1112 workInProgressRootFatalError = null;1113 workInProgressRootSkippedLanes = NoLanes;1114 workInProgressRootUpdatedLanes = NoLanes;1115 workInProgressRootPingedLanes = NoLanes;1116 if (enableSchedulerTracing) {1117 spawnedWorkDuringRender = null;1118 }1119 enableLog && console.log('prepareFreshStack end')1120}1121function handleError(root, thrownValue): void {1122 do {1123 let erroredWork = workInProgress;1124 try {1125 // Reset module-level state that was set during the render phase.1126 resetContextDependencies();1127 resetHooksAfterThrow();1128 // TODO: I found and added this missing line while investigating a1129 // separate issue. Write a regression test using string refs.1130 ReactCurrentOwner.current = null;1131 if (erroredWork === null || erroredWork.return === null) {1132 // Expected to be working on a non-root fiber. This is a fatal error1133 // because there's no ancestor that can handle it; the root is1134 // supposed to capture all errors that weren't caught by an error1135 // boundary.1136 workInProgressRootExitStatus = RootFatalErrored;1137 workInProgressRootFatalError = thrownValue;1138 // Set `workInProgress` to null. This represents advancing to the next1139 // sibling, or the parent if there are no siblings. But since the root1140 // has no siblings nor a parent, we set it to null. Usually this is1141 // handled by `completeUnitOfWork` or `unwindWork`, but since we're1142 // intentionally not calling those, we need set it here.1143 // TODO: Consider calling `unwindWork` to pop the contexts.1144 workInProgress = null;1145 return;1146 }1147 if (enableProfilerTimer && erroredWork.mode & ProfileMode) {1148 // Record the time spent rendering before an error was thrown. This1149 // avoids inaccurate Profiler durations in the case of a1150 // suspended render.1151 stopProfilerTimerIfRunningAndRecordDelta(erroredWork, true);1152 }1153 throwException(1154 root,1155 erroredWork.return,1156 erroredWork,1157 thrownValue,1158 workInProgressRootRenderLanes,1159 );1160 completeUnitOfWork(erroredWork);1161 } catch (yetAnotherThrownValue) {1162 // Something in the return path also threw.1163 thrownValue = yetAnotherThrownValue;1164 if (workInProgress === erroredWork && erroredWork !== null) {1165 // If this boundary has already errored, then we had trouble processing1166 // the error. Bubble it to the next boundary.1167 erroredWork = erroredWork.return;1168 workInProgress = erroredWork;1169 } else {1170 erroredWork = workInProgress;1171 }1172 continue;1173 }1174 // Return to the normal work loop.1175 return;1176 } while (true);1177}1178function pushDispatcher() {1179 const prevDispatcher = ReactCurrentDispatcher.current;1180 ReactCurrentDispatcher.current = ContextOnlyDispatcher;1181 if (prevDispatcher === null) {1182 // The React isomorphic package does not include a default dispatcher.1183 // Instead the first renderer will lazily attach one, in order to give1184 // nicer error messages.1185 return ContextOnlyDispatcher;1186 } else {1187 return prevDispatcher;1188 }1189}1190function popDispatcher(prevDispatcher) {1191 ReactCurrentDispatcher.current = prevDispatcher;1192}1193function pushInteractions(root) {1194 if (enableSchedulerTracing) {1195 const prevInteractions: Set<Interaction> | null = __interactionsRef.current;1196 __interactionsRef.current = root.memoizedInteractions;1197 return prevInteractions;1198 }1199 return null;1200}1201function popInteractions(prevInteractions) {1202 if (enableSchedulerTracing) {1203 __interactionsRef.current = prevInteractions;1204 }1205}1206export function markCommitTimeOfFallback() {1207 globalMostRecentFallbackTime = now();1208}1209export function markSkippedUpdateLanes(lane: Lane | Lanes): void {1210 workInProgressRootSkippedLanes = mergeLanes(1211 lane,1212 workInProgressRootSkippedLanes,1213 );1214}1215export function renderDidSuspend(): void {1216 if (workInProgressRootExitStatus === RootIncomplete) {1217 workInProgressRootExitStatus = RootSuspended;1218 }1219}1220export function renderDidSuspendDelayIfPossible(): void {1221 if (1222 workInProgressRootExitStatus === RootIncomplete ||1223 workInProgressRootExitStatus === RootSuspended1224 ) {1225 workInProgressRootExitStatus = RootSuspendedWithDelay;1226 }1227 // Check if there are updates that we skipped tree that might have unblocked1228 // this render.1229 if (1230 workInProgressRoot !== null &&1231 (includesNonIdleWork(workInProgressRootSkippedLanes) ||1232 includesNonIdleWork(workInProgressRootUpdatedLanes))1233 ) {1234 // Mark the current render as suspended so that we switch to working on1235 // the updates that were skipped. Usually we only suspend at the end of1236 // the render phase.1237 // TODO: We should probably always mark the root as suspended immediately1238 // (inside this function), since by suspending at the end of the render1239 // phase introduces a potential mistake where we suspend lanes that were1240 // pinged or updated while we were rendering.1241 markRootSuspended(workInProgressRoot, workInProgressRootRenderLanes);1242 }1243}1244export function renderDidError() {1245 if (workInProgressRootExitStatus !== RootCompleted) {1246 workInProgressRootExitStatus = RootErrored;1247 }1248}1249// Called during render to determine if anything has suspended.1250// Returns false if we're not sure.1251export function renderHasNotSuspendedYet(): boolean {1252 // If something errored or completed, we can't really be sure,1253 // so those are false.1254 return workInProgressRootExitStatus === RootIncomplete;1255}1256function renderRootSync(root: FiberRoot, lanes: Lanes) {1257 1258 enableLog && console.log('renderRootSync')1259 if (!__LOG_NAMES__.length || __LOG_NAMES__.includes('renderRootSync')) debugger1260 1261 const prevExecutionContext = executionContext;1262 executionContext |= RenderContext;1263 const prevDispatcher = pushDispatcher();1264 // If the root or lanes have changed, throw out the existing stack1265 // and prepare a fresh one. Otherwise we'll continue where we left off.1266 if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {1267 prepareFreshStack(root, lanes);1268 startWorkOnPendingInteractions(root, lanes);1269 }1270 const prevInteractions = pushInteractions(root);1271 do {1272 try {1273 workLoopSync();1274 break;1275 } catch (thrownValue) {1276 handleError(root, thrownValue);1277 }1278 } while (true);1279 resetContextDependencies();1280 if (enableSchedulerTracing) {1281 popInteractions(((prevInteractions: any): Set<Interaction>));1282 }1283 executionContext = prevExecutionContext;1284 popDispatcher(prevDispatcher);1285 if (workInProgress !== null) {1286 // This is a sync render, so we should have finished the whole tree.1287 invariant(1288 false,1289 'Cannot commit an incomplete root. This error is likely caused by a ' +1290 'bug in React. Please file an issue.',1291 );1292 }1293 // Set this to null to indicate there's no in-progress render.1294 workInProgressRoot = null;1295 workInProgressRootRenderLanes = NoLanes;1296 return workInProgressRootExitStatus;1297}1298// The work loop is an extremely hot path. Tell Closure not to inline it.1299/** @noinline */1300function workLoopSync() {1301 1302 enableLog && console.log('workLoopSync')1303 if (!__LOG_NAMES__.length || __LOG_NAMES__.includes('workLoopSync')) debugger1304 // Already timed out, so perform work without checking if we need to yield.1305 while (workInProgress !== null) {1306 enableLog && console.log('workLoopSync in while')1307 if (!__LOG_NAMES__.length || __LOG_NAMES__.includes('workLoopSync')) debugger1308 performUnitOfWork(workInProgress);1309 }1310}1311function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {1312 1313 enableLog && console.log('renderRootConcurrent start')1314 if (!__LOG_NAMES__.length || __LOG_NAMES__.includes('renderRootConcurrent')) debugger1315 const prevExecutionContext = executionContext;1316 executionContext |= RenderContext;1317 const prevDispatcher = pushDispatcher();1318 // If the root or lanes have changed, throw out the existing stack1319 // and prepare a fresh one. Otherwise we'll continue where we left off.1320 // rootæè
laneæ¹åï¼é½ä¼è°ç¨prepareFreshStackå·æ°æ 帧, 丢å¼ä¸ä¸æ¬¡æ¸²æè¿åº¦1321 if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {1322 resetRenderTimer();1323 // prepareFreshStackéé¢ä¼éè¿workInProgress = createWorkInProgress(root.current, null)å建workInProgress1324 prepareFreshStack(root, lanes);1325 startWorkOnPendingInteractions(root, lanes);...
ReactFiberWorkLoop.js
Source:ReactFiberWorkLoop.js
...612 (executionContext & (RenderContext | CommitContext)) === NoContext,613 'Should not already be working.',614 );615 flushPassiveEffects();616 let exitStatus = renderRootConcurrent(root, expirationTime);617 if (exitStatus !== RootIncomplete) {618 if (exitStatus === RootErrored) {619 // If something threw an error, try rendering one more time. We'll620 // render synchronously to block concurrent data mutations, and we'll621 // render at Idle (or lower) so that all pending updates are included.622 // If it still fails after the second attempt, we'll give up and commit623 // the resulting tree.624 expirationTime = expirationTime > Idle ? Idle : expirationTime;625 exitStatus = renderRootSync(root, expirationTime);626 }627 if (exitStatus === RootFatalErrored) {628 const fatalError = workInProgressRootFatalError;629 prepareFreshStack(root, expirationTime);630 markRootSuspendedAtTime(root, expirationTime);631 ensureRootIsScheduled(root);632 throw fatalError;633 }634 // We now have a consistent tree. The next step is either to commit it,635 // or, if something suspended, wait to commit it after a timeout.636 const finishedWork: Fiber = ((root.finishedWork =637 root.current.alternate): any);638 root.finishedExpirationTime = expirationTime;639 finishConcurrentRender(root, finishedWork, exitStatus, expirationTime);640 }641 ensureRootIsScheduled(root);642 if (root.callbackNode === originalCallbackNode) {643 // The task node scheduled for this root is the same one that's644 // currently executed. Need to return a continuation.645 return performConcurrentWorkOnRoot.bind(null, root);646 }647 return null;648}649function finishConcurrentRender(650 root,651 finishedWork,652 exitStatus,653 expirationTime,654) {655 switch (exitStatus) {656 case RootIncomplete:657 case RootFatalErrored: {658 invariant(false, 'Root did not complete. This is a bug in React.');659 }660 // Flow knows about invariant, so it complains if I add a break661 // statement, but eslint doesn't know about invariant, so it complains662 // if I do. eslint-disable-next-line no-fallthrough663 case RootErrored: {664 // We should have already attempted to retry this tree. If we reached665 // this point, it errored again. Commit it.666 commitRoot(root);667 break;668 }669 case RootSuspended: {670 markRootSuspendedAtTime(root, expirationTime);671 const lastSuspendedTime = root.lastSuspendedTime;672 if (expirationTime === lastSuspendedTime) {673 root.nextKnownPendingLevel = getRemainingExpirationTime(finishedWork);674 }675 // We have an acceptable loading state. We need to figure out if we676 // should immediately commit it or wait a bit.677 // If we have processed new updates during this render, we may now678 // have a new loading state ready. We want to ensure that we commit679 // that as soon as possible.680 const hasNotProcessedNewUpdates =681 workInProgressRootLatestProcessedExpirationTime === Sync;682 if (683 hasNotProcessedNewUpdates &&684 // do not delay if we're inside an act() scope685 !(686 __DEV__ &&687 flushSuspenseFallbacksInTests &&688 IsThisRendererActing.current689 )690 ) {691 // If we have not processed any new updates during this pass, then692 // this is either a retry of an existing fallback state or a693 // hidden tree. Hidden trees shouldn't be batched with other work694 // and after that's fixed it can only be a retry. We're going to695 // throttle committing retries so that we don't show too many696 // loading states too quickly.697 let msUntilTimeout =698 globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now();699 // Don't bother with a very short suspense time.700 if (msUntilTimeout > 10) {701 if (workInProgressRootHasPendingPing) {702 const lastPingedTime = root.lastPingedTime;703 if (lastPingedTime === NoWork || lastPingedTime >= expirationTime) {704 // This render was pinged but we didn't get to restart705 // earlier so try restarting now instead.706 root.lastPingedTime = expirationTime;707 prepareFreshStack(root, expirationTime);708 break;709 }710 }711 const nextTime = getNextRootExpirationTimeToWorkOn(root);712 if (nextTime !== NoWork && nextTime !== expirationTime) {713 // There's additional work on this root.714 break;715 }716 if (717 lastSuspendedTime !== NoWork &&718 lastSuspendedTime !== expirationTime719 ) {720 // We should prefer to render the fallback of at the last721 // suspended level. Ping the last suspended level to try722 // rendering it again.723 root.lastPingedTime = lastSuspendedTime;724 break;725 }726 // The render is suspended, it hasn't timed out, and there's no727 // lower priority work to do. Instead of committing the fallback728 // immediately, wait for more data to arrive.729 root.timeoutHandle = scheduleTimeout(730 commitRoot.bind(null, root),731 msUntilTimeout,732 );733 break;734 }735 }736 // The work expired. Commit immediately.737 commitRoot(root);738 break;739 }740 case RootSuspendedWithDelay: {741 markRootSuspendedAtTime(root, expirationTime);742 const lastSuspendedTime = root.lastSuspendedTime;743 if (expirationTime === lastSuspendedTime) {744 root.nextKnownPendingLevel = getRemainingExpirationTime(finishedWork);745 }746 if (747 // do not delay if we're inside an act() scope748 !(749 __DEV__ &&750 flushSuspenseFallbacksInTests &&751 IsThisRendererActing.current752 )753 ) {754 // We're suspended in a state that should be avoided. We'll try to755 // avoid committing it for as long as the timeouts let us.756 if (workInProgressRootHasPendingPing) {757 const lastPingedTime = root.lastPingedTime;758 if (lastPingedTime === NoWork || lastPingedTime >= expirationTime) {759 // This render was pinged but we didn't get to restart earlier760 // so try restarting now instead.761 root.lastPingedTime = expirationTime;762 prepareFreshStack(root, expirationTime);763 break;764 }765 }766 const nextTime = getNextRootExpirationTimeToWorkOn(root);767 if (nextTime !== NoWork && nextTime !== expirationTime) {768 // There's additional work on this root.769 break;770 }771 if (772 lastSuspendedTime !== NoWork &&773 lastSuspendedTime !== expirationTime774 ) {775 // We should prefer to render the fallback of at the last776 // suspended level. Ping the last suspended level to try777 // rendering it again.778 root.lastPingedTime = lastSuspendedTime;779 break;780 }781 let msUntilTimeout;782 if (workInProgressRootLatestSuspenseTimeout !== Sync) {783 // We have processed a suspense config whose expiration time we784 // can use as the timeout.785 msUntilTimeout =786 expirationTimeToMs(workInProgressRootLatestSuspenseTimeout) - now();787 } else if (workInProgressRootLatestProcessedExpirationTime === Sync) {788 // This should never normally happen because only new updates789 // cause delayed states, so we should have processed something.790 // However, this could also happen in an offscreen tree.791 msUntilTimeout = 0;792 } else {793 // If we don't have a suspense config, we're going to use a794 // heuristic to determine how long we can suspend.795 const eventTimeMs: number = inferTimeFromExpirationTime(796 workInProgressRootLatestProcessedExpirationTime,797 );798 const currentTimeMs = now();799 const timeUntilExpirationMs =800 expirationTimeToMs(expirationTime) - currentTimeMs;801 let timeElapsed = currentTimeMs - eventTimeMs;802 if (timeElapsed < 0) {803 // We get this wrong some time since we estimate the time.804 timeElapsed = 0;805 }806 msUntilTimeout = jnd(timeElapsed) - timeElapsed;807 // Clamp the timeout to the expiration time. TODO: Once the808 // event time is exact instead of inferred from expiration time809 // we don't need this.810 if (timeUntilExpirationMs < msUntilTimeout) {811 msUntilTimeout = timeUntilExpirationMs;812 }813 }814 // Don't bother with a very short suspense time.815 if (msUntilTimeout > 10) {816 // The render is suspended, it hasn't timed out, and there's no817 // lower priority work to do. Instead of committing the fallback818 // immediately, wait for more data to arrive.819 root.timeoutHandle = scheduleTimeout(820 commitRoot.bind(null, root),821 msUntilTimeout,822 );823 break;824 }825 }826 // The work expired. Commit immediately.827 commitRoot(root);828 break;829 }830 case RootCompleted: {831 // The work completed. Ready to commit.832 if (833 // do not delay if we're inside an act() scope834 !(835 __DEV__ &&836 flushSuspenseFallbacksInTests &&837 IsThisRendererActing.current838 ) &&839 workInProgressRootLatestProcessedExpirationTime !== Sync &&840 workInProgressRootCanSuspendUsingConfig !== null841 ) {842 // If we have exceeded the minimum loading delay, which probably843 // means we have shown a spinner already, we might have to suspend844 // a bit longer to ensure that the spinner is shown for845 // enough time.846 const msUntilTimeout = computeMsUntilSuspenseLoadingDelay(847 workInProgressRootLatestProcessedExpirationTime,848 expirationTime,849 workInProgressRootCanSuspendUsingConfig,850 );851 if (msUntilTimeout > 10) {852 markRootSuspendedAtTime(root, expirationTime);853 root.timeoutHandle = scheduleTimeout(854 commitRoot.bind(null, root),855 msUntilTimeout,856 );857 break;858 }859 }860 commitRoot(root);861 break;862 }863 default: {864 invariant(false, 'Unknown root exit status.');865 }866 }867}868// This is the entry point for synchronous tasks that don't go869// through Scheduler870function performSyncWorkOnRoot(root) {871 invariant(872 (executionContext & (RenderContext | CommitContext)) === NoContext,873 'Should not already be working.',874 );875 flushPassiveEffects();876 const lastExpiredTime = root.lastExpiredTime;877 let expirationTime;878 if (lastExpiredTime !== NoWork) {879 // There's expired work on this root. Check if we have a partial tree880 // that we can reuse.881 if (882 root === workInProgressRoot &&883 renderExpirationTime >= lastExpiredTime884 ) {885 // There's a partial tree with equal or greater than priority than the886 // expired level. Finish rendering it before rendering the rest of the887 // expired work.888 expirationTime = renderExpirationTime;889 } else {890 // Start a fresh tree.891 expirationTime = lastExpiredTime;892 }893 } else {894 // There's no expired work. This must be a new, synchronous render.895 expirationTime = Sync;896 }897 let exitStatus = renderRootSync(root, expirationTime);898 if (root.tag !== LegacyRoot && exitStatus === RootErrored) {899 // If something threw an error, try rendering one more time. We'll900 // render synchronously to block concurrent data mutations, and we'll901 // render at Idle (or lower) so that all pending updates are included.902 // If it still fails after the second attempt, we'll give up and commit903 // the resulting tree.904 expirationTime = expirationTime > Idle ? Idle : expirationTime;905 exitStatus = renderRootSync(root, expirationTime);906 }907 if (exitStatus === RootFatalErrored) {908 const fatalError = workInProgressRootFatalError;909 prepareFreshStack(root, expirationTime);910 markRootSuspendedAtTime(root, expirationTime);911 ensureRootIsScheduled(root);912 throw fatalError;913 }914 // We now have a consistent tree. Because this is a sync render, we915 // will commit it even if something suspended.916 root.finishedWork = (root.current.alternate: any);917 root.finishedExpirationTime = expirationTime;918 commitRoot(root);919 // Before exiting, make sure there's a callback scheduled for the next920 // pending level.921 ensureRootIsScheduled(root);922 return null;923}924export function flushRoot(root: FiberRoot, expirationTime: ExpirationTime) {925 markRootExpiredAtTime(root, expirationTime);926 ensureRootIsScheduled(root);927 if ((executionContext & (RenderContext | CommitContext)) === NoContext) {928 flushSyncCallbackQueue();929 }930}931export function flushDiscreteUpdates() {932 // TODO: Should be able to flush inside batchedUpdates, but not inside `act`.933 // However, `act` uses `batchedUpdates`, so there's no way to distinguish934 // those two cases. Need to fix this before exposing flushDiscreteUpdates935 // as a public API.936 if (937 (executionContext & (BatchedContext | RenderContext | CommitContext)) !==938 NoContext939 ) {940 if (__DEV__) {941 if ((executionContext & RenderContext) !== NoContext) {942 console.error(943 'unstable_flushDiscreteUpdates: Cannot flush updates when React is ' +944 'already rendering.',945 );946 }947 }948 // We're already rendering, so we can't synchronously flush pending work.949 // This is probably a nested event dispatch triggered by a lifecycle/effect,950 // like `el.focus()`. Exit.951 return;952 }953 flushPendingDiscreteUpdates();954 // If the discrete updates scheduled passive effects, flush them now so that955 // they fire before the next serial event.956 flushPassiveEffects();957}958export function deferredUpdates<A>(fn: () => A): A {959 // TODO: Remove in favor of Scheduler.next960 return runWithPriority(NormalPriority, fn);961}962export function syncUpdates<A, B, C, R>(963 fn: (A, B, C) => R,964 a: A,965 b: B,966 c: C,967): R {968 return runWithPriority(ImmediatePriority, fn.bind(null, a, b, c));969}970function flushPendingDiscreteUpdates() {971 if (rootsWithPendingDiscreteUpdates !== null) {972 // For each root with pending discrete updates, schedule a callback to973 // immediately flush them.974 const roots = rootsWithPendingDiscreteUpdates;975 rootsWithPendingDiscreteUpdates = null;976 roots.forEach((expirationTime, root) => {977 markRootExpiredAtTime(root, expirationTime);978 ensureRootIsScheduled(root);979 });980 // Now flush the immediate queue.981 flushSyncCallbackQueue();982 }983}984export function batchedUpdates<A, R>(fn: A => R, a: A): R {985 const prevExecutionContext = executionContext;986 executionContext |= BatchedContext;987 try {988 return fn(a);989 } finally {990 executionContext = prevExecutionContext;991 if (executionContext === NoContext) {992 // Flush the immediate callbacks that were scheduled during this batch993 flushSyncCallbackQueue();994 }995 }996}997export function batchedEventUpdates<A, R>(fn: A => R, a: A): R {998 const prevExecutionContext = executionContext;999 executionContext |= EventContext;1000 try {1001 return fn(a);1002 } finally {1003 executionContext = prevExecutionContext;1004 if (executionContext === NoContext) {1005 // Flush the immediate callbacks that were scheduled during this batch1006 flushSyncCallbackQueue();1007 }1008 }1009}1010export function discreteUpdates<A, B, C, D, R>(1011 fn: (A, B, C) => R,1012 a: A,1013 b: B,1014 c: C,1015 d: D,1016): R {1017 const prevExecutionContext = executionContext;1018 executionContext |= DiscreteEventContext;1019 try {1020 // Should this1021 return runWithPriority(UserBlockingPriority, fn.bind(null, a, b, c, d));1022 } finally {1023 executionContext = prevExecutionContext;1024 if (executionContext === NoContext) {1025 // Flush the immediate callbacks that were scheduled during this batch1026 flushSyncCallbackQueue();1027 }1028 }1029}1030export function unbatchedUpdates<A, R>(fn: (a: A) => R, a: A): R {1031 const prevExecutionContext = executionContext;1032 executionContext &= ~BatchedContext;1033 executionContext |= LegacyUnbatchedContext;1034 try {1035 return fn(a);1036 } finally {1037 executionContext = prevExecutionContext;1038 if (executionContext === NoContext) {1039 // Flush the immediate callbacks that were scheduled during this batch1040 flushSyncCallbackQueue();1041 }1042 }1043}1044export function flushSync<A, R>(fn: A => R, a: A): R {1045 if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {1046 invariant(1047 false,1048 'flushSync was called from inside a lifecycle method. It cannot be ' +1049 'called when React is already rendering.',1050 );1051 }1052 const prevExecutionContext = executionContext;1053 executionContext |= BatchedContext;1054 try {1055 return runWithPriority(ImmediatePriority, fn.bind(null, a));1056 } finally {1057 executionContext = prevExecutionContext;1058 // Flush the immediate callbacks that were scheduled during this batch.1059 // Note that this will happen even if batchedUpdates is higher up1060 // the stack.1061 flushSyncCallbackQueue();1062 }1063}1064export function flushControlled(fn: () => mixed): void {1065 const prevExecutionContext = executionContext;1066 executionContext |= BatchedContext;1067 try {1068 runWithPriority(ImmediatePriority, fn);1069 } finally {1070 executionContext = prevExecutionContext;1071 if (executionContext === NoContext) {1072 // Flush the immediate callbacks that were scheduled during this batch1073 flushSyncCallbackQueue();1074 }1075 }1076}1077function prepareFreshStack(root, expirationTime) {1078 root.finishedWork = null;1079 root.finishedExpirationTime = NoWork;1080 const timeoutHandle = root.timeoutHandle;1081 if (timeoutHandle !== noTimeout) {1082 // The root previous suspended and scheduled a timeout to commit a fallback1083 // state. Now that we have additional work, cancel the timeout.1084 root.timeoutHandle = noTimeout;1085 // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above1086 cancelTimeout(timeoutHandle);1087 }1088 if (workInProgress !== null) {1089 let interruptedWork = workInProgress.return;1090 while (interruptedWork !== null) {1091 unwindInterruptedWork(interruptedWork);1092 interruptedWork = interruptedWork.return;1093 }1094 }1095 workInProgressRoot = root;1096 workInProgress = createWorkInProgress(root.current, null);1097 renderExpirationTime = expirationTime;1098 workInProgressRootExitStatus = RootIncomplete;1099 workInProgressRootFatalError = null;1100 workInProgressRootLatestProcessedExpirationTime = Sync;1101 workInProgressRootLatestSuspenseTimeout = Sync;1102 workInProgressRootCanSuspendUsingConfig = null;1103 workInProgressRootNextUnprocessedUpdateTime = NoWork;1104 workInProgressRootHasPendingPing = false;1105 if (enableSchedulerTracing) {1106 spawnedWorkDuringRender = null;1107 }1108 if (__DEV__) {1109 ReactStrictModeWarnings.discardPendingWarnings();1110 }1111}1112function handleError(root, thrownValue) {1113 do {1114 try {1115 // Reset module-level state that was set during the render phase.1116 resetContextDependencies();1117 resetHooksAfterThrow();1118 resetCurrentDebugFiberInDEV();1119 if (workInProgress === null || workInProgress.return === null) {1120 // Expected to be working on a non-root fiber. This is a fatal error1121 // because there's no ancestor that can handle it; the root is1122 // supposed to capture all errors that weren't caught by an error1123 // boundary.1124 workInProgressRootExitStatus = RootFatalErrored;1125 workInProgressRootFatalError = thrownValue;1126 // Set `workInProgress` to null. This represents advancing to the next1127 // sibling, or the parent if there are no siblings. But since the root1128 // has no siblings nor a parent, we set it to null. Usually this is1129 // handled by `completeUnitOfWork` or `unwindWork`, but since we're1130 // interntionally not calling those, we need set it here.1131 // TODO: Consider calling `unwindWork` to pop the contexts.1132 workInProgress = null;1133 return null;1134 }1135 if (enableProfilerTimer && workInProgress.mode & ProfileMode) {1136 // Record the time spent rendering before an error was thrown. This1137 // avoids inaccurate Profiler durations in the case of a1138 // suspended render.1139 stopProfilerTimerIfRunningAndRecordDelta(workInProgress, true);1140 }1141 throwException(1142 root,1143 workInProgress.return,1144 workInProgress,1145 thrownValue,1146 renderExpirationTime,1147 );1148 workInProgress = completeUnitOfWork(workInProgress);1149 } catch (yetAnotherThrownValue) {1150 // Something in the return path also threw.1151 thrownValue = yetAnotherThrownValue;1152 continue;1153 }1154 // Return to the normal work loop.1155 return;1156 } while (true);1157}1158function pushDispatcher(root) {1159 const prevDispatcher = ReactCurrentDispatcher.current;1160 ReactCurrentDispatcher.current = ContextOnlyDispatcher;1161 if (prevDispatcher === null) {1162 // The React isomorphic package does not include a default dispatcher.1163 // Instead the first renderer will lazily attach one, in order to give1164 // nicer error messages.1165 return ContextOnlyDispatcher;1166 } else {1167 return prevDispatcher;1168 }1169}1170function popDispatcher(prevDispatcher) {1171 ReactCurrentDispatcher.current = prevDispatcher;1172}1173function pushInteractions(root) {1174 if (enableSchedulerTracing) {1175 const prevInteractions: Set<Interaction> | null = __interactionsRef.current;1176 __interactionsRef.current = root.memoizedInteractions;1177 return prevInteractions;1178 }1179 return null;1180}1181function popInteractions(prevInteractions) {1182 if (enableSchedulerTracing) {1183 __interactionsRef.current = prevInteractions;1184 }1185}1186export function markCommitTimeOfFallback() {1187 globalMostRecentFallbackTime = now();1188}1189export function markRenderEventTimeAndConfig(1190 expirationTime: ExpirationTime,1191 suspenseConfig: null | SuspenseConfig,1192): void {1193 if (1194 expirationTime < workInProgressRootLatestProcessedExpirationTime &&1195 expirationTime > Idle1196 ) {1197 workInProgressRootLatestProcessedExpirationTime = expirationTime;1198 }1199 if (suspenseConfig !== null) {1200 if (1201 expirationTime < workInProgressRootLatestSuspenseTimeout &&1202 expirationTime > Idle1203 ) {1204 workInProgressRootLatestSuspenseTimeout = expirationTime;1205 // Most of the time we only have one config and getting wrong is not bad.1206 workInProgressRootCanSuspendUsingConfig = suspenseConfig;1207 }1208 }1209}1210export function markUnprocessedUpdateTime(1211 expirationTime: ExpirationTime,1212): void {1213 if (expirationTime > workInProgressRootNextUnprocessedUpdateTime) {1214 workInProgressRootNextUnprocessedUpdateTime = expirationTime;1215 }1216}1217export function renderDidSuspend(): void {1218 if (workInProgressRootExitStatus === RootIncomplete) {1219 workInProgressRootExitStatus = RootSuspended;1220 }1221}1222export function renderDidSuspendDelayIfPossible(): void {1223 if (1224 workInProgressRootExitStatus === RootIncomplete ||1225 workInProgressRootExitStatus === RootSuspended1226 ) {1227 workInProgressRootExitStatus = RootSuspendedWithDelay;1228 }1229 // Check if there's a lower priority update somewhere else in the tree.1230 if (1231 workInProgressRootNextUnprocessedUpdateTime !== NoWork &&1232 workInProgressRoot !== null1233 ) {1234 // Mark the current render as suspended, and then mark that there's a1235 // pending update.1236 // TODO: This should immediately interrupt the current render, instead1237 // of waiting until the next time we yield.1238 markRootSuspendedAtTime(workInProgressRoot, renderExpirationTime);1239 markRootUpdatedAtTime(1240 workInProgressRoot,1241 workInProgressRootNextUnprocessedUpdateTime,1242 );1243 }1244}1245export function renderDidError() {1246 if (workInProgressRootExitStatus !== RootCompleted) {1247 workInProgressRootExitStatus = RootErrored;1248 }1249}1250// Called during render to determine if anything has suspended.1251// Returns false if we're not sure.1252export function renderHasNotSuspendedYet(): boolean {1253 // If something errored or completed, we can't really be sure,1254 // so those are false.1255 return workInProgressRootExitStatus === RootIncomplete;1256}1257function inferTimeFromExpirationTime(expirationTime: ExpirationTime): number {1258 // We don't know exactly when the update was scheduled, but we can infer an1259 // approximate start time from the expiration time.1260 const earliestExpirationTimeMs = expirationTimeToMs(expirationTime);1261 return earliestExpirationTimeMs - LOW_PRIORITY_EXPIRATION;1262}1263function inferTimeFromExpirationTimeWithSuspenseConfig(1264 expirationTime: ExpirationTime,1265 suspenseConfig: SuspenseConfig,1266): number {1267 // We don't know exactly when the update was scheduled, but we can infer an1268 // approximate start time from the expiration time by subtracting the timeout1269 // that was added to the event time.1270 const earliestExpirationTimeMs = expirationTimeToMs(expirationTime);1271 return (1272 earliestExpirationTimeMs -1273 (suspenseConfig.timeoutMs | 0 || LOW_PRIORITY_EXPIRATION)1274 );1275}1276function renderRootSync(root, expirationTime) {1277 const prevExecutionContext = executionContext;1278 executionContext |= RenderContext;1279 const prevDispatcher = pushDispatcher(root);1280 // If the root or expiration time have changed, throw out the existing stack1281 // and prepare a fresh one. Otherwise we'll continue where we left off.1282 if (root !== workInProgressRoot || expirationTime !== renderExpirationTime) {1283 prepareFreshStack(root, expirationTime);1284 startWorkOnPendingInteractions(root, expirationTime);1285 }1286 const prevInteractions = pushInteractions(root);1287 startWorkLoopTimer(workInProgress);1288 do {1289 try {1290 workLoopSync();1291 break;1292 } catch (thrownValue) {1293 handleError(root, thrownValue);1294 }1295 } while (true);1296 resetContextDependencies();1297 if (enableSchedulerTracing) {1298 popInteractions(((prevInteractions: any): Set<Interaction>));1299 }1300 executionContext = prevExecutionContext;1301 popDispatcher(prevDispatcher);1302 if (workInProgress !== null) {1303 // This is a sync render, so we should have finished the whole tree.1304 invariant(1305 false,1306 'Cannot commit an incomplete root. This error is likely caused by a ' +1307 'bug in React. Please file an issue.',1308 );1309 }1310 stopFinishedWorkLoopTimer();1311 // Set this to null to indicate there's no in-progress render.1312 workInProgressRoot = null;1313 return workInProgressRootExitStatus;1314}1315// The work loop is an extremely hot path. Tell Closure not to inline it.1316/** @noinline */1317function workLoopSync() {1318 // Already timed out, so perform work without checking if we need to yield.1319 while (workInProgress !== null) {1320 workInProgress = performUnitOfWork(workInProgress);1321 }1322}1323function renderRootConcurrent(root, expirationTime) {1324 const prevExecutionContext = executionContext;1325 executionContext |= RenderContext;1326 const prevDispatcher = pushDispatcher(root);1327 // If the root or expiration time have changed, throw out the existing stack1328 // and prepare a fresh one. Otherwise we'll continue where we left off.1329 if (root !== workInProgressRoot || expirationTime !== renderExpirationTime) {1330 prepareFreshStack(root, expirationTime);1331 startWorkOnPendingInteractions(root, expirationTime);1332 }1333 const prevInteractions = pushInteractions(root);1334 startWorkLoopTimer(workInProgress);1335 do {1336 try {1337 workLoopConcurrent();...
ReactFiberWorkLoop.old.js
Source:ReactFiberWorkLoop.old.js
...363 if (lanes === NoLanes) {364 // Defensive coding. This is never expected to happen.365 return null;366 }367 var exitStatus = renderRootConcurrent(root, lanes);368 if (includesSomeLane(workInProgressRootIncludedLanes, workInProgressRootUpdatedLanes)) {369 // The render included lanes that were updated during the render phase.370 // For example, when unhiding a hidden tree, we include all the lanes371 // that were previously skipped when the tree was hidden. That set of372 // lanes is a superset of the lanes we started rendering with.373 //374 // So we'll throw out the current work and restart.375 prepareFreshStack(root, NoLanes);376 } else if (exitStatus !== RootIncomplete) {377 if (exitStatus === RootErrored) {378 executionContext |= RetryAfterError; // If an error occurred during hydration,379 // discard server response and fall back to client side render.380 if (root.hydrate) {381 root.hydrate = false;382 clearContainer(root.containerInfo);383 } // If something threw an error, try rendering one more time. We'll render384 // synchronously to block concurrent data mutations, and we'll includes385 // all pending updates are included. If it still fails after the second386 // attempt, we'll give up and commit the resulting tree.387 lanes = getLanesToRetrySynchronouslyOnError(root);388 if (lanes !== NoLanes) {389 exitStatus = renderRootSync(root, lanes);390 }391 }392 if (exitStatus === RootFatalErrored) {393 var fatalError = workInProgressRootFatalError;394 prepareFreshStack(root, NoLanes);395 markRootSuspended$1(root, lanes);396 ensureRootIsScheduled(root, now());397 throw fatalError;398 } // We now have a consistent tree. The next step is either to commit it,399 // or, if something suspended, wait to commit it after a timeout.400 var finishedWork = root.current.alternate;401 root.finishedWork = finishedWork;402 root.finishedLanes = lanes;403 finishConcurrentRender(root, exitStatus, lanes);404 }405 ensureRootIsScheduled(root, now());406 if (root.callbackNode === originalCallbackNode) {407 // The task node scheduled for this root is the same one that's408 // currently executed. Need to return a continuation.409 return performConcurrentWorkOnRoot.bind(null, root);410 }411 return null;412 }413 function finishConcurrentRender(root, exitStatus, lanes) {414 switch (exitStatus) {415 case RootIncomplete:416 case RootFatalErrored:417 {418 {419 {420 throw Error( "Root did not complete. This is a bug in React." );421 }422 }423 }424 // Flow knows about invariant, so it complains if I add a break425 // statement, but eslint doesn't know about invariant, so it complains426 // if I do. eslint-disable-next-line no-fallthrough427 case RootErrored:428 {429 // We should have already attempted to retry this tree. If we reached430 // this point, it errored again. Commit it.431 commitRoot(root);432 break;433 }434 case RootSuspended:435 {436 markRootSuspended$1(root, lanes); // We have an acceptable loading state. We need to figure out if we437 // should immediately commit it or wait a bit.438 if (includesOnlyRetries(lanes) && // do not delay if we're inside an act() scope439 !shouldForceFlushFallbacksInDEV()) {440 // This render only included retries, no updates. Throttle committing441 // retries so that we don't show too many loading states too quickly.442 var msUntilTimeout = globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now(); // Don't bother with a very short suspense time.443 if (msUntilTimeout > 10) {444 var nextLanes = getNextLanes(root, NoLanes);445 if (nextLanes !== NoLanes) {446 // There's additional work on this root.447 break;448 }449 var suspendedLanes = root.suspendedLanes;450 if (!isSubsetOfLanes(suspendedLanes, lanes)) {451 // We should prefer to render the fallback of at the last452 // suspended level. Ping the last suspended level to try453 // rendering it again.454 // FIXME: What if the suspended lanes are Idle? Should not restart.455 var eventTime = requestEventTime();456 markRootPinged(root, suspendedLanes);457 break;458 } // The render is suspended, it hasn't timed out, and there's no459 // lower priority work to do. Instead of committing the fallback460 // immediately, wait for more data to arrive.461 root.timeoutHandle = scheduleTimeout(commitRoot.bind(null, root), msUntilTimeout);462 break;463 }464 } // The work expired. Commit immediately.465 commitRoot(root);466 break;467 }468 case RootSuspendedWithDelay:469 {470 markRootSuspended$1(root, lanes);471 if (includesOnlyTransitions(lanes)) {472 // This is a transition, so we should exit without committing a473 // placeholder and without scheduling a timeout. Delay indefinitely474 // until we receive more data.475 break;476 }477 if (!shouldForceFlushFallbacksInDEV()) {478 // This is not a transition, but we did trigger an avoided state.479 // Schedule a placeholder to display after a short delay, using the Just480 // Noticeable Difference.481 // TODO: Is the JND optimization worth the added complexity? If this is482 // the only reason we track the event time, then probably not.483 // Consider removing.484 var mostRecentEventTime = getMostRecentEventTime(root, lanes);485 var eventTimeMs = mostRecentEventTime;486 var timeElapsedMs = now() - eventTimeMs;487 var _msUntilTimeout = jnd(timeElapsedMs) - timeElapsedMs; // Don't bother with a very short suspense time.488 if (_msUntilTimeout > 10) {489 // Instead of committing the fallback immediately, wait for more data490 // to arrive.491 root.timeoutHandle = scheduleTimeout(commitRoot.bind(null, root), _msUntilTimeout);492 break;493 }494 } // Commit the placeholder.495 commitRoot(root);496 break;497 }498 case RootCompleted:499 {500 // The work completed. Ready to commit.501 commitRoot(root);502 break;503 }504 default:505 {506 {507 {508 throw Error( "Unknown root exit status." );509 }510 }511 }512 }513 }514 function markRootSuspended$1(root, suspendedLanes) {515 // When suspending, we should always exclude lanes that were pinged or (more516 // rarely, since we try to avoid it) updated during the render phase.517 // TODO: Lol maybe there's a better way to factor this besides this518 // obnoxiously named function :)519 suspendedLanes = removeLanes(suspendedLanes, workInProgressRootPingedLanes);520 suspendedLanes = removeLanes(suspendedLanes, workInProgressRootUpdatedLanes);521 markRootSuspended(root, suspendedLanes);522 } // This is the entry point for synchronous tasks that don't go523 // through Scheduler524 function performSyncWorkOnRoot(root) {525 if (!((executionContext & (RenderContext | CommitContext)) === NoContext)) {526 {527 throw Error( "Should not already be working." );528 }529 }530 flushPassiveEffects();531 var lanes;532 var exitStatus;533 if (root === workInProgressRoot && includesSomeLane(root.expiredLanes, workInProgressRootRenderLanes)) {534 // There's a partial tree, and at least one of its lanes has expired. Finish535 // rendering it before rendering the rest of the expired work.536 lanes = workInProgressRootRenderLanes;537 exitStatus = renderRootSync(root, lanes);538 if (includesSomeLane(workInProgressRootIncludedLanes, workInProgressRootUpdatedLanes)) {539 // The render included lanes that were updated during the render phase.540 // For example, when unhiding a hidden tree, we include all the lanes541 // that were previously skipped when the tree was hidden. That set of542 // lanes is a superset of the lanes we started rendering with.543 //544 // Note that this only happens when part of the tree is rendered545 // concurrently. If the whole tree is rendered synchronously, then there546 // are no interleaved events.547 lanes = getNextLanes(root, lanes);548 exitStatus = renderRootSync(root, lanes);549 }550 } else {551 lanes = getNextLanes(root, NoLanes);552 exitStatus = renderRootSync(root, lanes);553 }554 if (root.tag !== LegacyRoot && exitStatus === RootErrored) {555 executionContext |= RetryAfterError; // If an error occurred during hydration,556 // discard server response and fall back to client side render.557 if (root.hydrate) {558 root.hydrate = false;559 clearContainer(root.containerInfo);560 } // If something threw an error, try rendering one more time. We'll render561 // synchronously to block concurrent data mutations, and we'll includes562 // all pending updates are included. If it still fails after the second563 // attempt, we'll give up and commit the resulting tree.564 lanes = getLanesToRetrySynchronouslyOnError(root);565 if (lanes !== NoLanes) {566 exitStatus = renderRootSync(root, lanes);567 }568 }569 if (exitStatus === RootFatalErrored) {570 var fatalError = workInProgressRootFatalError;571 prepareFreshStack(root, NoLanes);572 markRootSuspended$1(root, lanes);573 ensureRootIsScheduled(root, now());574 throw fatalError;575 } // We now have a consistent tree. Because this is a sync render, we576 // will commit it even if something suspended.577 var finishedWork = root.current.alternate;578 root.finishedWork = finishedWork;579 root.finishedLanes = lanes;580 commitRoot(root); // Before exiting, make sure there's a callback scheduled for the next581 // pending level.582 ensureRootIsScheduled(root, now());583 return null;584 }585 function flushRoot(root, lanes) {586 markRootExpired(root, lanes);587 ensureRootIsScheduled(root, now());588 if ((executionContext & (RenderContext | CommitContext)) === NoContext) {589 resetRenderTimer();590 flushSyncCallbackQueue();591 }592 }593 function getExecutionContext() {594 return executionContext;595 }596 // flush reactäºä»¶597 function flushDiscreteUpdates() {598 // TODO: Should be able to flush inside batchedUpdates, but not inside `act`.599 // However, `act` uses `batchedUpdates`, so there's no way to distinguish600 // those two cases. Need to fix this before exposing flushDiscreteUpdates601 // as a public API.602 if ((executionContext & (BatchedContext | RenderContext | CommitContext)) !== NoContext) {603 {604 if ((executionContext & RenderContext) !== NoContext) {605 error('unstable_flushDiscreteUpdates: Cannot flush updates when React is ' + 'already rendering.');606 }607 } // We're already rendering, so we can't synchronously flush pending work.608 // This is probably a nested event dispatch triggered by a lifecycle/effect,609 // like `el.focus()`. Exit.610 return;611 }612 flushPendingDiscreteUpdates(); // If the discrete updates scheduled passive effects, flush them now so that613 // they fire before the next serial event.614 flushPassiveEffects();615 }616 function flushPendingDiscreteUpdates() {617 if (rootsWithPendingDiscreteUpdates !== null) {618 // For each root with pending discrete updates, schedule a callback to619 // immediately flush them.620 var roots = rootsWithPendingDiscreteUpdates;621 rootsWithPendingDiscreteUpdates = null;622 roots.forEach(function (root) {623 markDiscreteUpdatesExpired(root);624 ensureRootIsScheduled(root, now());625 });626 } // Now flush the immediate queue.627 flushSyncCallbackQueue();628 }629 function batchedUpdates$1(fn, a) {630 var prevExecutionContext = executionContext;631 executionContext |= BatchedContext;632 try {633 return fn(a);634 } finally {635 executionContext = prevExecutionContext;636 if (executionContext === NoContext) {637 // Flush the immediate callbacks that were scheduled during this batch638 resetRenderTimer();639 flushSyncCallbackQueue();640 }641 }642 }643 function batchedEventUpdates$1(fn, a) {644 var prevExecutionContext = executionContext;645 executionContext |= EventContext;646 try {647 return fn(a);648 } finally {649 executionContext = prevExecutionContext;650 if (executionContext === NoContext) {651 // Flush the immediate callbacks that were scheduled during this batch652 resetRenderTimer();653 flushSyncCallbackQueue();654 }655 }656 }657 function discreteUpdates$1(fn, a, b, c, d) {658 var prevExecutionContext = executionContext;659 executionContext |= DiscreteEventContext;660 {661 try {662 return runWithPriority$1(UserBlockingPriority$2, fn.bind(null, a, b, c, d));663 } finally {664 executionContext = prevExecutionContext;665 if (executionContext === NoContext) {666 // Flush the immediate callbacks that were scheduled during this batch667 resetRenderTimer();668 flushSyncCallbackQueue();669 }670 }671 }672 }673 // éæ¹éæ´æ°674 function unbatchedUpdates(fn, a) {675 var prevExecutionContext = executionContext;676 executionContext &= ~BatchedContext;677 executionContext |= LegacyUnbatchedContext;678 try {679 return fn(a);680 } finally {681 executionContext = prevExecutionContext;682 if (executionContext === NoContext) {683 // Flush the immediate callbacks that were scheduled during this batch684 resetRenderTimer();685 flushSyncCallbackQueue();686 }687 }688 }689 function flushSync(fn, a) {690 var prevExecutionContext = executionContext;691 if ((prevExecutionContext & (RenderContext | CommitContext)) !== NoContext) {692 {693 error('flushSync was called from inside a lifecycle method. React cannot ' + 'flush when React is already rendering. Consider moving this call to ' + 'a scheduler task or micro task.');694 }695 return fn(a);696 }697 executionContext |= BatchedContext;698 {699 try {700 if (fn) {701 return runWithPriority$1(ImmediatePriority$1, fn.bind(null, a));702 } else {703 return undefined;704 }705 } finally {706 executionContext = prevExecutionContext; // Flush the immediate callbacks that were scheduled during this batch.707 // Note that this will happen even if batchedUpdates is higher up708 // the stack.709 flushSyncCallbackQueue();710 }711 }712 }713 function flushControlled(fn) {714 var prevExecutionContext = executionContext;715 executionContext |= BatchedContext;716 {717 try {718 runWithPriority$1(ImmediatePriority$1, fn);719 } finally {720 executionContext = prevExecutionContext;721 if (executionContext === NoContext) {722 // Flush the immediate callbacks that were scheduled during this batch723 resetRenderTimer();724 flushSyncCallbackQueue();725 }726 }727 }728 }729 function pushRenderLanes(fiber, lanes) {730 push(subtreeRenderLanesCursor, subtreeRenderLanes, fiber);731 subtreeRenderLanes = mergeLanes(subtreeRenderLanes, lanes);732 workInProgressRootIncludedLanes = mergeLanes(workInProgressRootIncludedLanes, lanes);733 }734 function popRenderLanes(fiber) {735 subtreeRenderLanes = subtreeRenderLanesCursor.current;736 pop(subtreeRenderLanesCursor, fiber);737 }738 function prepareFreshStack(root, lanes) {739 root.finishedWork = null;740 root.finishedLanes = NoLanes;741 var timeoutHandle = root.timeoutHandle;742 if (timeoutHandle !== noTimeout) {743 // The root previous suspended and scheduled a timeout to commit a fallback744 // state. Now that we have additional work, cancel the timeout.745 root.timeoutHandle = noTimeout; // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above746 cancelTimeout(timeoutHandle);747 }748 if (workInProgress !== null) {749 var interruptedWork = workInProgress.return;750 while (interruptedWork !== null) {751 unwindInterruptedWork(interruptedWork);752 interruptedWork = interruptedWork.return;753 }754 }755 workInProgressRoot = root;756 workInProgress = createWorkInProgress(root.current, null);757 workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;758 workInProgressRootExitStatus = RootIncomplete;759 workInProgressRootFatalError = null;760 workInProgressRootSkippedLanes = NoLanes;761 workInProgressRootUpdatedLanes = NoLanes;762 workInProgressRootPingedLanes = NoLanes;763 {764 spawnedWorkDuringRender = null;765 }766 {767 ReactStrictModeWarnings.discardPendingWarnings();768 }769 }770 function handleError(root, thrownValue) {771 do {772 var erroredWork = workInProgress;773 try {774 // Reset module-level state that was set during the render phase.775 resetContextDependencies();776 resetHooksAfterThrow();777 resetCurrentFiber(); // TODO: I found and added this missing line while investigating a778 // separate issue. Write a regression test using string refs.779 ReactCurrentOwner$2.current = null;780 if (erroredWork === null || erroredWork.return === null) {781 // Expected to be working on a non-root fiber. This is a fatal error782 // because there's no ancestor that can handle it; the root is783 // supposed to capture all errors that weren't caught by an error784 // boundary.785 workInProgressRootExitStatus = RootFatalErrored;786 workInProgressRootFatalError = thrownValue; // Set `workInProgress` to null. This represents advancing to the next787 // sibling, or the parent if there are no siblings. But since the root788 // has no siblings nor a parent, we set it to null. Usually this is789 // handled by `completeUnitOfWork` or `unwindWork`, but since we're790 // intentionally not calling those, we need set it here.791 // TODO: Consider calling `unwindWork` to pop the contexts.792 workInProgress = null;793 return;794 }795 if (enableProfilerTimer && erroredWork.mode & ProfileMode) {796 // Record the time spent rendering before an error was thrown. This797 // avoids inaccurate Profiler durations in the case of a798 // suspended render.799 stopProfilerTimerIfRunningAndRecordDelta(erroredWork, true);800 }801 throwException(root, erroredWork.return, erroredWork, thrownValue, workInProgressRootRenderLanes);802 completeUnitOfWork(erroredWork);803 } catch (yetAnotherThrownValue) {804 // Something in the return path also threw.805 thrownValue = yetAnotherThrownValue;806 if (workInProgress === erroredWork && erroredWork !== null) {807 // If this boundary has already errored, then we had trouble processing808 // the error. Bubble it to the next boundary.809 erroredWork = erroredWork.return;810 workInProgress = erroredWork;811 } else {812 erroredWork = workInProgress;813 }814 continue;815 } // Return to the normal work loop.816 return;817 } while (true);818 }819 function pushDispatcher() {820 var prevDispatcher = ReactCurrentDispatcher$2.current;821 ReactCurrentDispatcher$2.current = ContextOnlyDispatcher;822 if (prevDispatcher === null) {823 // The React isomorphic package does not include a default dispatcher.824 // Instead the first renderer will lazily attach one, in order to give825 // nicer error messages.826 return ContextOnlyDispatcher;827 } else {828 return prevDispatcher;829 }830 }831 function popDispatcher(prevDispatcher) {832 ReactCurrentDispatcher$2.current = prevDispatcher;833 }834 function pushInteractions(root) {835 {836 var prevInteractions = __interactionsRef.current;837 __interactionsRef.current = root.memoizedInteractions;838 return prevInteractions;839 }840 }841 function popInteractions(prevInteractions) {842 {843 __interactionsRef.current = prevInteractions;844 }845 }846 function markCommitTimeOfFallback() {847 globalMostRecentFallbackTime = now();848 }849 function markSkippedUpdateLanes(lane) {850 workInProgressRootSkippedLanes = mergeLanes(lane, workInProgressRootSkippedLanes);851 }852 function renderDidSuspend() {853 if (workInProgressRootExitStatus === RootIncomplete) {854 workInProgressRootExitStatus = RootSuspended;855 }856 }857 function renderDidSuspendDelayIfPossible() {858 if (workInProgressRootExitStatus === RootIncomplete || workInProgressRootExitStatus === RootSuspended) {859 workInProgressRootExitStatus = RootSuspendedWithDelay;860 } // Check if there are updates that we skipped tree that might have unblocked861 // this render.862 if (workInProgressRoot !== null && (includesNonIdleWork(workInProgressRootSkippedLanes) || includesNonIdleWork(workInProgressRootUpdatedLanes))) {863 // Mark the current render as suspended so that we switch to working on864 // the updates that were skipped. Usually we only suspend at the end of865 // the render phase.866 // TODO: We should probably always mark the root as suspended immediately867 // (inside this function), since by suspending at the end of the render868 // phase introduces a potential mistake where we suspend lanes that were869 // pinged or updated while we were rendering.870 markRootSuspended$1(workInProgressRoot, workInProgressRootRenderLanes);871 }872 }873 function renderDidError() {874 if (workInProgressRootExitStatus !== RootCompleted) {875 workInProgressRootExitStatus = RootErrored;876 }877 } // Called during render to determine if anything has suspended.878 // Returns false if we're not sure.879 function renderHasNotSuspendedYet() {880 // If something errored or completed, we can't really be sure,881 // so those are false.882 return workInProgressRootExitStatus === RootIncomplete;883 }884 function renderRootSync(root, lanes) {885 var prevExecutionContext = executionContext;886 executionContext |= RenderContext;887 var prevDispatcher = pushDispatcher(); // If the root or lanes have changed, throw out the existing stack888 // and prepare a fresh one. Otherwise we'll continue where we left off.889 if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {890 prepareFreshStack(root, lanes);891 startWorkOnPendingInteractions(root, lanes);892 }893 var prevInteractions = pushInteractions(root);894 {895 markRenderStarted(lanes);896 }897 do {898 try {899 workLoopSync();900 break;901 } catch (thrownValue) {902 handleError(root, thrownValue);903 }904 } while (true);905 resetContextDependencies();906 {907 popInteractions(prevInteractions);908 }909 executionContext = prevExecutionContext;910 popDispatcher(prevDispatcher);911 if (workInProgress !== null) {912 // This is a sync render, so we should have finished the whole tree.913 {914 {915 throw Error( "Cannot commit an incomplete root. This error is likely caused by a bug in React. Please file an issue." );916 }917 }918 }919 {920 markRenderStopped();921 } // Set this to null to indicate there's no in-progress render.922 workInProgressRoot = null;923 workInProgressRootRenderLanes = NoLanes;924 return workInProgressRootExitStatus;925 } // The work loop is an extremely hot path. Tell Closure not to inline it.926 /** @noinline */927 function workLoopSync() {928 // Already timed out, so perform work without checking if we need to yield.929 while (workInProgress !== null) {930 performUnitOfWork(workInProgress);931 }932 }933 function renderRootConcurrent(root, lanes) {934 var prevExecutionContext = executionContext;935 executionContext |= RenderContext;936 var prevDispatcher = pushDispatcher(); // If the root or lanes have changed, throw out the existing stack937 // and prepare a fresh one. Otherwise we'll continue where we left off.938 if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {939 resetRenderTimer();940 prepareFreshStack(root, lanes);941 startWorkOnPendingInteractions(root, lanes);942 }943 var prevInteractions = pushInteractions(root);944 {945 markRenderStarted(lanes);946 }947 do {...
FiberWorkLoop.js
Source:FiberWorkLoop.js
...139 let lanes = getNextLanes(140 root,141 root === wipRoot ? wipRootRenderLanes : NoLanes,142 )143 let exitStatus = renderRootConcurrent(root, lanes); 144 if(exitStatus !== RootIncomplete){145 if(exitStatus === RootErrored){146 executionContext |= RootErrored;147 return null;148 }149 // now we have a consistent tree and ready to commit.150 const finishedWork = root.current.alternate151 root.finishedWork = finishedWork;152 root.finishedLanes = lanes;153 finishConcurrentRender(root, exitStatus, lanes);154 }155 //schedule new tasks found in Render Phase156 ensureRootIsScheduled(root, performance.now());157 // root.callbackNode is always relevant to a task which hasn't completely 158 // finished due to expiration or some other reasons and it will be set to 159 // null in Commit Phase.160 if (root.callbackNode === originalCallbackNode){161 return performConcurrentWorkOnRoot.bind(null, root);162 }163 return null;164}165function finishConcurrentRender(root, exitStatus, lanes){166 switch (exitStatus){167 case RootCompleted:{168 commitRoot(root);169 break;170 }171 case RootSuspendedWithDelay:172 case RootSuspended:{ 173 markRootSuspended(root, lanes);174 // work expired. Commit immediately.175 commitRoot(root);176 break;177 }178 }179}180export function pushRenderLanes(fiber, lanes){181 push(subtreeRenderLanesCursor, subtreeRenderLanes);182 subtreeRenderLanes = mergeLanes(subtreeRenderLanes, lanes);183 wipRootIncludedLanes = mergeLanes(184 wipRootIncludedLanes,185 lanes,186 );187}188export function popRenderLanes(){189 subtreeRenderLanes = subtreeRenderLanesCursor.current;190 pop(subtreeRenderLanesCursor);191}192function prepareFreshStack(root, lanes){193 if (wip !== null){194 console.error('Leftover work found:', wip);195 }196 wipRoot = root;197 wip = createWorkInProgress(root.current);198 wipRootRenderLanes = subtreeRenderLanes = wipRootIncludedLanes = lanes;199 wipRootExitStatus = RootIncomplete;200 wipRootSkippedLanes = wipRootUpdatedLanes = wipRootPingedLanes = NoLanes;201}202function handleError(root, thrownValue){203 let erroredWork = wip;204 try {205 throwException(206 root,207 erroredWork.return,208 erroredWork,209 thrownValue,210 wipRootRenderLanes211 );212 completeUnitOfWork(erroredWork);213 } catch (yetAnotherThrownValue){214 console.error(yetAnotherThrownValue);215 }216}217export function markSkippedUpdateLanes(lane){218 wipRootSkippedLanes = mergeLanes(219 lane, 220 wipRootSkippedLanes,221 )222}223export function renderDidSuspend(){224 if(wipRootExitStatus === RootIncomplete){225 wipRootExitStatus = RootSuspended;226 }227}228export function renderDidSuspendDelayIfPossible(){229 if(230 wipRootExitStatus === RootIncomplete ||231 wipRootExitStatus === RootSuspended232 ){233 wipRootExitStatus = RootSuspendedWithDelay;234 }235 if(236 wipRoot !== null &&237 (includesNonIdleWork(wipRootSkippedLanes) ||238 includesNonIdleWork(wipRootUpdatedLanes))239 ){240 markRootSuspended(wipRoot, wipRootRenderLanes);241 }242}243export function renderDidError(){244 if (wipRootExitStatus !== RootCompleted){245 wipRootExitStatus = RootErrored;246 }247}248function renderRootConcurrent(root, lanes){249 const prevExecutionContext = executionContext;250 executionContext |= RenderContext;251 // If the root or lanes have changed, throw out the existing stack252 // and prepare a fresh one. Otherwise we'll continue where we left off.253 if (wipRoot !== root || wipRootRenderLanes !== lanes){254 //create a new FiberNode by cloning root.current and set it to wip.255 prepareFreshStack(root, lanes);256 }257 //Keep trying until all caught errors handled.258 do{259 try {260 workLoopConcurrent();261 break;262 } catch(thrownValue){...
SchedulerHostConfig.default.js
Source:SchedulerHostConfig.default.js
1/**2 * Copyright (c) Facebook, Inc. and its affiliates.3 *4 * This source code is licensed under the MIT license found in the5 * LICENSE file in the root directory of this source tree.6 */7import {enableIsInputPending} from '../SchedulerFeatureFlags';8export let requestHostCallback;9export let cancelHostCallback;10export let requestHostTimeout;11export let cancelHostTimeout;12export let shouldYieldToHost;13export let requestPaint;14export let getCurrentTime;15export let forceFrameRate;16const hasPerformanceNow =17 typeof performance === 'object' && typeof performance.now === 'function';18if (hasPerformanceNow) {19 const localPerformance = performance;20 // è·åä»æå¼å§å°ç°å¨çæ¶é´21 getCurrentTime = () => localPerformance.now();22} else {23 const localDate = Date;24 const initialTime = localDate.now();25 // è·åä»æå¼å§å°ç°å¨çæ¶é´26 getCurrentTime = () => localDate.now() - initialTime;27}28if (29 // If Scheduler runs in a non-DOM environment, it falls back to a naive30 // implementation using setTimeout.31 typeof window === 'undefined' ||32 // Check if MessageChannel is supported, too.33 typeof MessageChannel !== 'function'34) {35 // If this accidentally gets imported in a non-browser environment, e.g. JavaScriptCore,36 // fallback to a naive implementation.37 let _callback = null;38 let _timeoutID = null;39 const _flushCallback = function() {40 if (_callback !== null) {41 try {42 const currentTime = getCurrentTime();43 const hasRemainingTime = true;44 _callback(hasRemainingTime, currentTime);45 _callback = null;46 } catch (e) {47 setTimeout(_flushCallback, 0);48 throw e;49 }50 }51 };52 requestHostCallback = function(cb) {53 if (_callback !== null) {54 // Protect against re-entrancy.55 setTimeout(requestHostCallback, 0, cb);56 } else {57 _callback = cb;58 setTimeout(_flushCallback, 0);59 }60 };61 cancelHostCallback = function() {62 _callback = null;63 };64 requestHostTimeout = function(cb, ms) {65 _timeoutID = setTimeout(cb, ms);66 };67 cancelHostTimeout = function() {68 clearTimeout(_timeoutID);69 };70 shouldYieldToHost = function() {71 return false;72 };73 requestPaint = forceFrameRate = function() {};74} else {75 // Capture local references to native APIs, in case a polyfill overrides them.76 // fiberæ¶æå¼æ¥æ´æ°ä¾èµæµè§å¨åçapi window.requestAnimationFrame window.cancelAnimationFrame77 const setTimeout = window.setTimeout;78 const clearTimeout = window.clearTimeout;79 if (typeof console !== 'undefined') {80 // TODO: Scheduler no longer requires these methods to be polyfilled. But81 // maybe we want to continue warning if they don't exist, to preserve the82 // option to rely on it in the future?83 const requestAnimationFrame = window.requestAnimationFrame;84 const cancelAnimationFrame = window.cancelAnimationFrame;85 if (typeof requestAnimationFrame !== 'function') {86 // Using console['error'] to evade Babel and ESLint87 console['error'](88 "This browser doesn't support requestAnimationFrame. " +89 'Make sure that you load a ' +90 'polyfill in older browsers. https://reactjs.org/link/react-polyfills',91 );92 }93 if (typeof cancelAnimationFrame !== 'function') {94 // Using console['error'] to evade Babel and ESLint95 console['error'](96 "This browser doesn't support cancelAnimationFrame. " +97 'Make sure that you load a ' +98 'polyfill in older browsers. https://reactjs.org/link/react-polyfills',99 );100 }101 }102 let isMessageLoopRunning = false;103 let scheduledHostCallback = null;104 let taskTimeoutID = -1;105 // Scheduler periodically yields in case there is other work on the main106 // thread, like user events. By default, it yields multiple times per frame.107 // It does not attempt to align with frame boundaries, since most tasks don't108 // need to be frame aligned; for those that do, use requestAnimationFrame.109 // æ¯å¸§çæ¶é´è®¾ç½®ä¸º5ms110 let yieldInterval = 5;111 let deadline = 0;112 // TODO: Make this configurable113 // TODO: Adjust this based on priority?114 const maxYieldInterval = 300;115 let needsPaint = false;116 // å
¼å®¹æ§å¤çnavigator.scheduling117 // 设置shouldYieldToHostårequestPaint118 if (119 enableIsInputPending &&120 navigator !== undefined &&121 navigator.scheduling !== undefined &&122 navigator.scheduling.isInputPending !== undefined123 ) {124 const scheduling = navigator.scheduling;125 // æ¯å¦éè¦ç§»äº¤æ§å¶ç»æµè§å¨126 // è¶
æ¶/éè¦éç»ä¸ºtrueï¼å¦å为false127 shouldYieldToHost = function() {128 const currentTime = getCurrentTime();129 if (currentTime >= deadline) {130 // There's no time left. We may want to yield control of the main131 // thread, so the browser can perform high priority tasks. The main ones132 // are painting and user input. If there's a pending paint or a pending133 // input, then we should yield. But if there's neither, then we can134 // yield less often while remaining responsive. We'll eventually yield135 // regardless, since there could be a pending paint that wasn't136 // accompanied by a call to `requestPaint`, or other main thread tasks137 // like network events.138 if (needsPaint || scheduling.isInputPending()) {139 // There is either a pending paint or a pending input.140 return true;141 }142 // There's no pending input. Only yield if we've reached the max143 // yield interval.144 return currentTime >= maxYieldInterval;145 } else {146 // There's still time left in the frame.147 return false;148 }149 };150 requestPaint = function() {151 needsPaint = true;152 };153 } else {154 // `isInputPending` is not available. Since we have no way of knowing if155 // there's pending input, always yield at the end of the frame.156 shouldYieldToHost = function() {157 return getCurrentTime() >= deadline;158 };159 // Since we yield every frame regardless, `requestPaint` has no effect.160 requestPaint = function() {};161 }162 // fpsåªæ¯æ0å°125163 forceFrameRate = function(fps) {164 if (fps < 0 || fps > 125) {165 // Using console['error'] to evade Babel and ESLint166 console['error'](167 'forceFrameRate takes a positive int between 0 and 125, ' +168 'forcing frame rates higher than 125 fps is not supported',169 );170 return;171 }172 if (fps > 0) {173 yieldInterval = Math.floor(1000 / fps);174 } else {175 // reset the framerate176 yieldInterval = 5;177 }178 };179 // è¿éä¼å¾ªç¯æ§è¡ scheduledHostCallback => flushWork => workLoopï¼ç´å°å¼æ¥æ´æ°å®taskQueueä¸çææä»»å¡180 const performWorkUntilDeadline = () => {181 if (scheduledHostCallback !== null) {182 const currentTime = getCurrentTime();183 // Yield after `yieldInterval` ms, regardless of where we are in the vsync184 // cycle. This means there's always time remaining at the beginning of185 // the message event.186 // å½åæ¶é´(ä»æå¼å§å°ç°å¨çæ¶é´) å ä¸ æ¯å¸§çç»reactçæ¶é´(ä¸è¬ä¸º5ms)ï¼å³ä¸ºè¶
æ¶æ¶é´187 // ä¹å°±æ¯å®ä¹è¶
æ¶æ¶é´deadline为å½åçåºç¡ä¸å ä¸ æ¯å¸§çç»reactçæ¶é´(ä¸è¬ä¸º5ms)188 // è¿ä¸ªè¶
æ¶æ¶é´ç¨å¨shouldYieldToHostä¸ï¼ç¨äºå¤ææ¯å¦éè¦ç§»äº¤æ§å¶ç»æµè§å¨189 deadline = currentTime + yieldInterval;190 const hasTimeRemaining = true;191 try {192 // taskQueue被ä¸æçè¯ï¼è¿é为trueï¼å¦å为false193 const hasMoreWork = scheduledHostCallback(194 hasTimeRemaining,195 currentTime,196 );197 if (!hasMoreWork) {198 // taskQueueä¸æ²¡æä»»å¡ï¼æ è®°isMessageLoopRunning为falseï¼éç½®scheduledHostCallback为nullï¼ç»æ循ç¯199 isMessageLoopRunning = false;200 scheduledHostCallback = null;201 } else {202 // If there's more work, schedule the next message event at the end203 // of the preceding one.204 // taskQueue被ä¸æï¼ç»§ç»è§¦åperformWorkUntilDeadline205 // è¿éä¼ä¸ç´è°ç¨ scheduledHostCallback => flushWork => workLoopï¼ç´å°å¼æ¥æ´æ°å®taskQueueä¸çææä»»å¡206 // å
·ä½æµç¨207 // performWorkUntilDeadline => scheduledHostCallback(ä¹å°±æ¯flushWork)208 // => workLoop(éåæ§è¡taskQueueä¸çä»»å¡ï¼ä¹å°±æ¯performConcurrentWorkOnRoot)209 // => renderRootConcurrent => workLoopConcurrent(è¿éå°±ä¼å¤ææ¯å¦éè¦ç§»äº¤æ§å¶æï¼ç¶åå³å®æ¯å¦æ§è¡performUnitOfWork)210 // => å¦æä¸æï¼taskQueueä¸çperformConcurrentWorkOnRoot对åºçtaskä»»å¡ä¸ä¼æ¸
é¤ï¼åæ¶è·³åºæ´æ°ï¼è¿åtrueå°hasMoreWork => port.postMessage(null) å®ä»»å¡ => 触åä¸ä¸æ¬¡performWorkUntilDeadline => ...211 // å¦æ没æä¸æï¼è¿åfalseå°hasMoreWorkï¼ç»ætaskQueueä¸çæ´æ°ãå¦æætimeQueueï¼ä¼å¨å»¶æ¶å°äºä¹åæ¨å
¥taskQueueå¼å§æ´æ°212 port.postMessage(null);213 }214 } catch (error) {215 // If a scheduler task throws, exit the current browser task so the216 // error can be observed.217 port.postMessage(null);218 throw error;219 }220 } else {221 // 没æscheduledHostCallbackï¼æ è®°isMessageLoopRunning为falseï¼ç»æ循ç¯222 isMessageLoopRunning = false;223 }224 // Yielding to the browser will give it a chance to paint, so we can225 // reset this.226 // è¿é设置needsPaint为falseï¼è¿ä¸ªneedsPaintç¨å¨shouldYieldToHostä¸ï¼ç¨äºå¤ææ¯å¦éè¦ç§»äº¤æ§å¶ç»æµè§å¨227 needsPaint = false;228 };229 // å建ä¸ä¸ªæ°çæ¶æ¯ééï¼å¹¶éè¿å®ç两个MessagePortå±æ§åéæ°æ®230 // å®ä»»å¡231 const channel = new MessageChannel();232 const port = channel.port2;233 // onmessageçæ§è¡å®é
å¾éè¦234 // reactç»å次performWorkUntilDeadlineçæ¶é´åªæ5msï¼ä¸æ¦è¶
è¿5msï¼å¿
须移交æ§å¶æç»æµè§å¨ï¼æµè§å¨å¤çrequestAnimationFrameã页é¢æ¸²æç»å¶235 // çå°æµè§å¨èªå·±çå·¥ä½å®æäºï¼ä¼æ§è¡å®ä»»å¡ï¼ä¹å°±æ¯ä¸ä¸ä¸ªperformWorkUntilDeadline236 // reactç¨è¿ç§æ¹å¼æ¨¡æäºrequestIdleCallback(ç±äºå
¼å®¹æ§é®é¢æ²¡æéç¨)237 // ä¿è¯ç»æµè§å¨å帧çå·¥ä½æ¶é´ç论ä¸æ¯ 1000/60-5=11.67ms(60Hzå·æ°ç)238 // ä½æ¯æ æ³ä¿è¯å次performWorkUntilDeadlineä¸çæåä¸ä¸ªåå
ä»»å¡çç»ææ¶é´ä¼è¶
è¿5mså¤ä¹
ï¼ç论ä¸åæåå
ä»»å¡ä¹åä¸ä¼è¶
åº5mså¾å¤239 // èçç»æµè§å¨ç11.67mså
¶å®æ¯æä½éç(æµè§å¨ä¸éè¦è¿ä¹ä¹
)ï¼ææç论ä¸æ¯ä¸ä¼é æ页é¢å¡é¡¿240 channel.port1.onmessage = performWorkUntilDeadline;241 // 设置requestHostCallbackï¼å¹¶è§¦åperformWorkUntilDeadline242 requestHostCallback = function(callback) {243 scheduledHostCallback = callback;244 if (!isMessageLoopRunning) {245 isMessageLoopRunning = true;246 port.postMessage(null);247 }248 };249 cancelHostCallback = function() {250 scheduledHostCallback = null;251 };252 // 设置taskTimeoutIDï¼å»¶æ¶msåæ§è¡callback253 requestHostTimeout = function(callback, ms) {254 taskTimeoutID = setTimeout(() => {255 callback(getCurrentTime());256 }, ms);257 };258 cancelHostTimeout = function() {259 clearTimeout(taskTimeoutID);260 taskTimeoutID = -1;261 };...
env.js
Source:env.js
1const fs = require('fs');2const path = require('path');3const paths = require('./paths');4// Make sure that including paths.js after env.js will read .env variables.5delete require.cache[require.resolve('./paths')];6const NODE_ENV = process.env.NODE_ENV;7if (!NODE_ENV) {8 throw new Error(9 'The NODE_ENV environment variable is required but was not specified.'10 );11}12// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use13const dotenvFiles = [14 `${paths.dotenv}.${NODE_ENV}.local`,15 `${paths.dotenv}.${NODE_ENV}`,16 // Don't include `.env.local` for `test` environment17 // since normally you expect tests to produce the same18 // results for everyone19 NODE_ENV !== 'test' && `${paths.dotenv}.local`,20 paths.dotenv,21].filter(Boolean);22// Load environment variables from .env* files. Suppress warnings using silent23// if this file is missing. dotenv will never modify any environment variables24// that have already been set. Variable expansion is supported in .env files.25// https://github.com/motdotla/dotenv26// https://github.com/motdotla/dotenv-expand27dotenvFiles.forEach(dotenvFile => {28 if (fs.existsSync(dotenvFile)) {29 require('dotenv-expand')(30 require('dotenv').config({31 path: dotenvFile,32 })33 );34 }35});36// We support resolving modules according to `NODE_PATH`.37// This lets you use absolute paths in imports inside large monorepos:38// https://github.com/facebook/create-react-app/issues/253.39// It works similar to `NODE_PATH` in Node itself:40// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders41// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.42// Otherwise, we risk importing Node.js core modules into an app instead of webpack shims.43// https://github.com/facebook/create-react-app/issues/1023#issuecomment-26534442144// We also resolve them to make sure all tools using them work consistently.45const appDirectory = fs.realpathSync(process.cwd());46process.env.NODE_PATH = (process.env.NODE_PATH || '')47 .split(path.delimiter)48 .filter(folder => folder && !path.isAbsolute(folder))49 .map(folder => path.resolve(appDirectory, folder))50 .join(path.delimiter);51// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be52// injected into the application via DefinePlugin in webpack configuration.53const REACT_APP = /^REACT_APP_/i;54function getClientEnvironment(publicUrl) {55 const raw = Object.keys(process.env)56 .filter(key => REACT_APP.test(key))57 .reduce(58 (env, key) => {59 env[key] = process.env[key];60 return env;61 },62 {63 // Useful for determining whether weâre running in production mode.64 // Most importantly, it switches React into the correct mode.65 NODE_ENV: process.env.NODE_ENV || 'development',66 // Useful for resolving the correct path to static assets in `public`.67 // For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />.68 // This should only be used as an escape hatch. Normally you would put69 // images into the `src` and `import` them in code to get their paths.70 PUBLIC_URL: publicUrl,71 // We support configuring the sockjs pathname during development.72 // These settings let a developer run multiple simultaneous projects.73 // They are used as the connection `hostname`, `pathname` and `port`74 // in webpackHotDevClient. They are used as the `sockHost`, `sockPath`75 // and `sockPort` options in webpack-dev-server.76 WDS_SOCKET_HOST: process.env.WDS_SOCKET_HOST,77 WDS_SOCKET_PATH: process.env.WDS_SOCKET_PATH,78 WDS_SOCKET_PORT: process.env.WDS_SOCKET_PORT,79 }80 );81 // Stringify all values so we can feed into webpack DefinePlugin82 const stringified = {83 'process.env': Object.keys(raw).reduce((env, key) => {84 env[key] = JSON.stringify(raw[key]);85 return env;86 }, {}),87 "__DEV__": false,88 "__PROFILE__": true,89 "__EXPERIMENTAL__": true,90 "__UMD__": true,91 __NEW_RECONCILER__: true,92 '__LOG_NAMES__': JSON.stringify([93 // 'createRoot',94 // 'ReactDOMRoot',95 // 'createRootImpl',96 // 'createContainer',97 // 'createFiberRoot',98 // 'createHostRootFiber',99 // 'createFiber',100 // 'FiberNode',101 // 'initializeUpdateQueue',102 // 'markContainerAsRoot',103 // 'listenToAllSupportedEvents',104 // 'jsx',105 'render',106 // 'updateContainer',107 // 'enqueueUpdate',108 // 'scheduleUpdateOnFiber',109 // 'ensureRootIsScheduled',110 // 'unstable_scheduleCallback',111 // 'requestHostCallback',112 // 'performWorkUntilDeadline',113 // 'flushWork',114 // 'workLoop',115 // 'performConcurrentWorkOnRoot',116 // 'flushPassiveEffects',117 // 'renderRootConcurrent',118 // 'prepareFreshStack',119 // 'createWorkInProgress',120 // 'createFiber',121 // 'FiberNode',122 // 'performUnitOfWork',123 // 'beginWork',124 // 'setInitialDOMProperties',125 // 'setInitialProperties',126 // 'diffProperties',127 // 'dispatchEvent',128 // 'mountIndeterminateComponent',129 // 'renderWithHooks',130 'useState',131 // 'mountState',132 // 'mountWorkInProgressHook',133 // 'updateHostRoot',134 // 'cloneUpdateQueue',135 // 'processUpdateQueue',136 // 'getStateFromUpdate',137 // 'reconcileChildren',138 // 'reconcileChildFibers',139 // 'reconcileChildrenArray',140 // 'createChild',141 // 'mountChildFibers',142 // 'createFiberFromElement',143 // 'createFiberFromTypeAndProps',144 // 'completeUnitOfWork',145 // 'completeWork',146 // 'commitRootImpl',147 // 'commitBeforeMutationEffects',148 // 'commitBeforeMutationEffectsImpl',149 // 'commitBeforeMutationLifeCycles',150 // 'clearContainer',151 // 'commitMutationEffectsImpl',152 // 'commitPlacement',153 // 'getHostParentFiber',154 // 'getHostSibling',155 // 'insertOrAppendPlacementNodeIntoContainer',156 // 'insertOrAppendPlacementNode',157 // 'trapClickOnNonInteractiveElement',158 // 'resetAfterCommit',159 // 'restoreSelection',160 // 'recursivelyCommitLayoutEffects',161 // 'ensureRootIsScheduled',162 // 'createInstance',163 // 'createElement',164 // 'updateFiberProps',165 // 'bubbleProperties',166 // 'dispatchDiscreteEvent',167 // 'createEventListenerWrapperWithPriority',168 'updateWorkInProgressHook'169 ]),170 };171 return { raw, stringified };172}...
render.js
Source:render.js
...24///////////////////////////// 25// å¨Fiberä¸å¹¶åæ§è¡26function performConcurrentWorkOnRoot() {27 // render é¶æ®µ28 renderRootConcurrent();29 // commit é¶æ®µ30 commitRoot(root)31}32function renderRootConcurrent() {33 do {34 try {35 workLoopConcurrent();36 break;37 } catch (thrownValue) {38 handleError(root, thrownValue);39 }40 } while (true);41}42function workLoopConcurrent() {43 while (workInProgress !== null && !shouldYield()) {44 performUnitOfWork(workInProgress);45 }46}...
Using AI Code Generation
1const playwright = require('playwright');2(async () => {3 const browser = await playwright.chromium.launch();4 const context = await browser.newContext();5 const page = await context.newPage();6 const html = await page.renderRootConcurrent();7 console.log(html);8 await browser.close();9})();10import { chromium } from 'playwright';11(async () => {12 const browser = await chromium.launch();13 const context = await browser.newContext();14 const page = await context.newPage();15 const html = await page.renderRootConcurrent();16 console.log(html);17 await browser.close();18})();
Using AI Code Generation
1const playwright = require('playwright');2(async () => {3 const browser = await playwright.chromium.launch();4 const page = await browser.newPage();5 await page.renderRootConcurrent();6 await browser.close();7})();8const playwright = require('playwright');9(async () => {10 const browser = await playwright.chromium.launch();11 const page = await browser.newPage();12 await page.renderRootConcurrent('screenshot.png');13 await browser.close();14})();15const playwright = require('playwright');16(async () => {17 const browser = await playwright.chromium.launch();18 const page = await browser.newPage();19 await page.renderRootConcurrent('screenshot.png');20 await browser.close();21})();22const playwright = require('playwright');23(async () => {24 const browser = await playwright.chromium.launch();25 const page = await browser.newPage();26 await page.renderRootConcurrent('screenshot.png');27 await browser.close();28})();
Using AI Code Generation
1const playwright = require('playwright');2(async () => {3 const browser = await playwright.chromium.launch();4 const page = await browser.newPage();5 const content = await page.renderRootConcurrent();6 console.log(content);7 await browser.close();8})();9const playwright = require('playwright');10(async () => {11 const browser = await playwright.chromium.launch();12 const page = await browser.newPage();13 const content = await page.renderRoot();14 console.log(content);15 await browser.close();16})();17const playwright = require('playwright');18(async () => {19 const browser = await playwright.chromium.launch();20 const page = await browser.newPage();21 const content = await page.render();22 console.log(content);23 await browser.close();24})();25const playwright = require('playwright');26(async () => {27 const browser = await playwright.chromium.launch();28 const page = await browser.newPage();29 const content = await page.render();30 console.log(content);31 await browser.close();32})();33const playwright = require('playwright');34(async () => {35 const browser = await playwright.chromium.launch();36 const page = await browser.newPage();37 const content = await page.render();38 console.log(content);39 await browser.close();40})();41const playwright = require('playwright');42(async () => {43 const browser = await playwright.chromium.launch();44 const page = await browser.newPage();45 const content = await page.render();46 console.log(content);47 await browser.close();48})();49const playwright = require('playwright');50(async () => {51 const browser = await playwright.chromium.launch();52 const page = await browser.newPage();53 const content = await page.render();54 console.log(content);55 await browser.close();56})();57const playwright = require('playwright');58(async () => {
Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch();4 const context = await browser.newContext({ headless: false });5 const page = await context.newPage();6 const { html } = await page.renderRootConcurrent();7 console.log(html);8 await browser.close();9})();
Using AI Code Generation
1const { renderRootConcurrent } = require('playwright/lib/server/supplements/recorder/recorderSupplement');2const { renderRootConcurrent } = require('playwright/lib/server/supplements/recorder/recorderSupplement');3const { renderRootConcurrent } = require('playwright/lib/server/supplements/recorder/recorderSupplement');4const { renderRootConcurrent } = require('playwright/lib/server/supplements/recorder/recorderSupplement');5const { renderRootConcurrent } = require('playwright/lib/server/supplements/recorder/recorderSupplement');6const { renderRootConcurrent } = require('playwright/lib/server/supplements/recorder/recorderSupplement');7const { renderRootConcurrent } = require('playwright/lib/server/supplements/recorder/recorderSupplement');8const { renderRootConcurrent } = require('playwright/lib/server/supplements/recorder/recorderSupplement');9const { renderRootConcurrent } = require('playwright/lib/server/supplements/recorder/recorderSupplement');10const { renderRootConcurrent } = require('playwright/lib/server/supplements/recorder/recorderSupplement');11const { renderRootConcurrent } = require('playwright/lib/server/supplements/recorder/recorderSupplement');12const { renderRootConcurrent } = require('playwright/lib/server/supplements/recorder/recorderSupplement');13const { renderRootConcurrent } = require('playwright/lib/server/supplements/recorder/recorderSupplement');14const { renderRoot
Using AI Code Generation
1const { chromium } = require('playwright');2const playwright = require('playwright/lib/server/playwright');3const { renderRootConcurrent } = playwright;4(async () => {5 const browser = await chromium.launch({6 });7 const context = await browser.newContext();8 const page = await context.newPage();9 const root = await page.document();10 const element = await root.querySelector('h1');11 const result = await renderRootConcurrent(root);12 console.log(result);13 await browser.close();14})();15const { chromium } = require('playwright');16const playwright = require('playwright/lib/server/playwright');17const { renderRootConcurrent } = playwright;18(async () => {19 const browser = await chromium.launch({20 });21 const context = await browser.newContext();22 const page = await context.newPage();23 const root = await page.document();24 const element = await root.querySelector('h1');25 const result = await renderRootConcurrent(root);26 console.log(result);27 await browser.close();28})();
LambdaTest’s Playwright tutorial will give you a broader idea about the Playwright automation framework, its unique features, and use cases with examples to exceed your understanding of Playwright testing. This tutorial will give A to Z guidance, from installing the Playwright framework to some best practices and advanced concepts.
Get 100 minutes of automation test minutes FREE!!