Best JavaScript code snippet using playwright-internal
ReactFiberWorkLoop.js
Source:ReactFiberWorkLoop.js
...1041 if (workInProgressRootExitStatus !== RootCompleted) {1042 workInProgressRootExitStatus = RootErrored;1043 }1044}1045function inferTimeFromExpirationTime(1046 expirationTime: ExpirationTime,1047 suspenseConfig: null | SuspenseConfig,1048): number {1049 // We don't know exactly when the update was scheduled, but we can infer an1050 // approximate start time from the expiration time.1051 const earliestExpirationTimeMs = expirationTimeToMs(expirationTime);1052 return (1053 earliestExpirationTimeMs -1054 (suspenseConfig !== null1055 ? suspenseConfig.timeoutMs | 0 || LOW_PRIORITY_EXPIRATION1056 : LOW_PRIORITY_EXPIRATION)1057 );1058}1059function workLoopSync() {1060 // Already timed out, so perform work without checking if we need to yield.1061 // å·²ç»è¶
æ¶äºï¼æ以æ§è¡å·¥ä½æ¶ä¸è¦æ£æ¥æ¯å¦éè¦è®©æ¥ã1062 while (workInProgress !== null) {1063 workInProgress = performUnitOfWork(workInProgress);1064 }1065}1066function workLoop() {1067 // Perform work until Scheduler asks us to yield1068 // æ§è¡å·¥ä½ï¼ç´å°è°åº¦å¨è¦æ±æ们让æ¥1069 while (workInProgress !== null && !shouldYield()) {1070 workInProgress = performUnitOfWork(workInProgress);1071 }1072}1073function performUnitOfWork(unitOfWork: Fiber): Fiber | null {1074 // The current, flushed, state of this fiber is the alternate. Ideally1075 // nothing should rely on this, but relying on it here means that we don't1076 // need an additional field on the work in progress.1077 // 该 fiber çå½åå²å·ç¶ææ¯äº¤æ¿çã1078 // çæ³æ
åµä¸ä¸åºè¯¥ä¾èµäºæ¤ï¼ä½æ¯ä¾èµäºæ¤æå³çæ们ä¸éè¦å¯¹æ£å¨è¿è¡çå·¥ä½è¿è¡é¢å¤çç 究ã1079 const current = unitOfWork.alternate;1080 startWorkTimer(unitOfWork);1081 setCurrentDebugFiberInDEV(unitOfWork);1082 let next;1083 if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {1084 startProfilerTimer(unitOfWork);1085 next = beginWork(current, unitOfWork, renderExpirationTime);1086 stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);1087 } else {1088 next = beginWork(current, unitOfWork, renderExpirationTime);1089 }1090 resetCurrentDebugFiberInDEV();1091 unitOfWork.memoizedProps = unitOfWork.pendingProps;1092 if (next === null) {1093 // If this doesn't spawn new work, complete the current work.1094 // å¦æè¿æ²¡æ产çæ°çå·¥ä½ï¼å®æå½åçå·¥ä½ã1095 next = completeUnitOfWork(unitOfWork);1096 }1097 ReactCurrentOwner.current = null;1098 return next;1099}1100function completeUnitOfWork(unitOfWork: Fiber): Fiber | null {1101 // Attempt to complete the current unit of work, then move to the next1102 // sibling. If there are no more siblings, return to the parent fiber.1103 // å°è¯å®æå½åçå·¥ä½åå
ï¼ç¶å移å¨å°ä¸ä¸ä¸ªå级ã1104 // å¦æ没æå
å¼èç¹ï¼å°±åå° parent fiberã1105 workInProgress = unitOfWork;1106 do {1107 // The current, flushed, state of this fiber is the alternate. Ideally1108 // nothing should rely on this, but relying on it here means that we don't1109 // need an additional field on the work in progress.1110 // 该 fiber çå½åå²å·ç¶ææ¯äº¤æ¿çã1111 // çæ³æ
åµä¸ä¸åºè¯¥ä¾èµäºæ¤ï¼ä½æ¯ä¾èµäºæ¤æå³çæ们ä¸éè¦å¯¹æ£å¨è¿è¡çå·¥ä½è¿è¡é¢å¤çç 究ã1112 const current = workInProgress.alternate;1113 const returnFiber = workInProgress.return;1114 // Check if the work completed or if something threw.1115 // æ£æ¥å·¥ä½æ¯å¦å®æææ¯å¦æä¸è¥¿æåºã1116 if ((workInProgress.effectTag & Incomplete) === NoEffect) {1117 setCurrentDebugFiberInDEV(workInProgress);1118 let next;1119 if (1120 !enableProfilerTimer ||1121 (workInProgress.mode & ProfileMode) === NoMode1122 ) {1123 next = completeWork(current, workInProgress, renderExpirationTime);1124 } else {1125 startProfilerTimer(workInProgress);1126 next = completeWork(current, workInProgress, renderExpirationTime);1127 // Update render duration assuming we didn't error.1128 stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);1129 }1130 stopWorkTimer(workInProgress);1131 resetCurrentDebugFiberInDEV();1132 resetChildExpirationTime(workInProgress);1133 if (next !== null) {1134 // Completing this fiber spawned new work. Work on that next.1135 // å®æè¿ä¸ª fiber 产çäºæ°çå·¥ä½ãä¸ä¸ä¸ªå·¥ä½ã1136 return next;1137 }1138 if (1139 returnFiber !== null &&1140 // Do not append effects to parents if a sibling failed to complete1141 (returnFiber.effectTag & Incomplete) === NoEffect1142 ) {1143 // Append all the effects of the subtree and this fiber onto the effect1144 // list of the parent. The completion order of the children affects the1145 // side-effect order.1146 if (returnFiber.firstEffect === null) {1147 returnFiber.firstEffect = workInProgress.firstEffect;1148 }1149 if (workInProgress.lastEffect !== null) {1150 if (returnFiber.lastEffect !== null) {1151 returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;1152 }1153 returnFiber.lastEffect = workInProgress.lastEffect;1154 }1155 // If this fiber had side-effects, we append it AFTER the children's1156 // side-effects. We can perform certain side-effects earlier if needed,1157 // by doing multiple passes over the effect list. We don't want to1158 // schedule our own side-effect on our own list because if end up1159 // reusing children we'll schedule this effect onto itself since we're1160 // at the end.1161 const effectTag = workInProgress.effectTag;1162 // Skip both NoWork and PerformedWork tags when creating the effect1163 // list. PerformedWork effect is read by React DevTools but shouldn't be1164 // committed.1165 if (effectTag > PerformedWork) {1166 if (returnFiber.lastEffect !== null) {1167 returnFiber.lastEffect.nextEffect = workInProgress;1168 } else {1169 returnFiber.firstEffect = workInProgress;1170 }1171 returnFiber.lastEffect = workInProgress;1172 }1173 }1174 } else {1175 // This fiber did not complete because something threw. Pop values off1176 // the stack without entering the complete phase. If this is a boundary,1177 // capture values if possible.1178 // è¿ä¸ª fiber 没æå®ææ¯å 为æä¸è¥¿æåºäºã1179 // å¨ä¸è¿å
¥å®æ´é¶æ®µçæ
åµä¸ä»å æ ä¸ååºå¼ã1180 // å¦æè¿æ¯ä¸ä¸ªè¾¹çï¼åå°½å¯è½æè·å¼ã1181 const next = unwindWork(workInProgress, renderExpirationTime);1182 // Because this fiber did not complete, don't reset its expiration time.1183 // å 为è¿ä¸ª fiber 没æå®æï¼æ以ä¸è¦éç½®å®çè¿ææ¶é´ã1184 if (1185 enableProfilerTimer &&1186 (workInProgress.mode & ProfileMode) !== NoMode1187 ) {1188 // Record the render duration for the fiber that errored.1189 stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);1190 // Include the time spent working on failed children before continuing.1191 let actualDuration = workInProgress.actualDuration;1192 let child = workInProgress.child;1193 while (child !== null) {1194 actualDuration += child.actualDuration;1195 child = child.sibling;1196 }1197 workInProgress.actualDuration = actualDuration;1198 }1199 if (next !== null) {1200 // If completing this work spawned new work, do that next. We'll come1201 // back here again.1202 // Since we're restarting, remove anything that is not a host effect1203 // from the effect tag.1204 // TODO: The name stopFailedWorkTimer is misleading because Suspense1205 // also captures and restarts.1206 // å¦æå®æè¿é¡¹å·¥ä½äº§çäºæ°çå·¥ä½ï¼é£ä¹æ¥ä¸æ¥å°±å»åã1207 // æ们è¿ä¼åå°è¿éã1208 // å 为æ们æ£å¨éæ°å¯å¨ï¼æ以ä»æææ ç¾ä¸å é¤ææä¸æ¯ host effect çå
容ã1209 // TODO: stopFailedWorkTimerè¿ä¸ªå称å
·æ误导æ§ï¼å 为æ¬å¿µä¹ä¼æè·åéæ°å¯å¨ã1210 stopFailedWorkTimer(workInProgress);1211 next.effectTag &= HostEffectMask;1212 return next;1213 }1214 stopWorkTimer(workInProgress);1215 if (returnFiber !== null) {1216 // Mark the parent fiber as incomplete and clear its effect list.1217 // å° parent fiber æ 记为ä¸å®æ´ï¼å¹¶æ¸
é¤å
¶ä½ç¨å表ã1218 returnFiber.firstEffect = returnFiber.lastEffect = null;1219 returnFiber.effectTag |= Incomplete;1220 }1221 }1222 const siblingFiber = workInProgress.sibling;1223 if (siblingFiber !== null) {1224 // If there is more work to do in this returnFiber, do that next.1225 // å¦æå¨è¿æ¹é¢è¿ææ´å¤çå·¥ä½è¦åï¼é£å°±æ¥çåã1226 return siblingFiber;1227 }1228 // Otherwise, return to the parent1229 // å¦åï¼è¿åå°ç¶è¿ç¨1230 workInProgress = returnFiber;1231 } while (workInProgress !== null);1232 // We've reached the root.1233 // æ们已ç»å°äºæ ¹èç¹ã1234 if (workInProgressRootExitStatus === RootIncomplete) {1235 workInProgressRootExitStatus = RootCompleted;1236 }1237 return null;1238}1239function resetChildExpirationTime(completedWork: Fiber) {1240 if (1241 renderExpirationTime !== Never &&1242 completedWork.childExpirationTime === Never1243 ) {1244 // The children of this component are hidden. Don't bubble their1245 // expiration times.1246 return;1247 }1248 let newChildExpirationTime = NoWork;1249 // Bubble up the earliest expiration time.1250 if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoMode) {1251 // In profiling mode, resetChildExpirationTime is also used to reset1252 // profiler durations.1253 let actualDuration = completedWork.actualDuration;1254 let treeBaseDuration = completedWork.selfBaseDuration;1255 // When a fiber is cloned, its actualDuration is reset to 0. This value will1256 // only be updated if work is done on the fiber (i.e. it doesn't bailout).1257 // When work is done, it should bubble to the parent's actualDuration. If1258 // the fiber has not been cloned though, (meaning no work was done), then1259 // this value will reflect the amount of time spent working on a previous1260 // render. In that case it should not bubble. We determine whether it was1261 // cloned by comparing the child pointer.1262 const shouldBubbleActualDurations =1263 completedWork.alternate === null ||1264 completedWork.child !== completedWork.alternate.child;1265 let child = completedWork.child;1266 while (child !== null) {1267 const childUpdateExpirationTime = child.expirationTime;1268 const childChildExpirationTime = child.childExpirationTime;1269 if (childUpdateExpirationTime > newChildExpirationTime) {1270 newChildExpirationTime = childUpdateExpirationTime;1271 }1272 if (childChildExpirationTime > newChildExpirationTime) {1273 newChildExpirationTime = childChildExpirationTime;1274 }1275 if (shouldBubbleActualDurations) {1276 actualDuration += child.actualDuration;1277 }1278 treeBaseDuration += child.treeBaseDuration;1279 child = child.sibling;1280 }1281 completedWork.actualDuration = actualDuration;1282 completedWork.treeBaseDuration = treeBaseDuration;1283 } else {1284 let child = completedWork.child;1285 while (child !== null) {1286 const childUpdateExpirationTime = child.expirationTime;1287 const childChildExpirationTime = child.childExpirationTime;1288 if (childUpdateExpirationTime > newChildExpirationTime) {1289 newChildExpirationTime = childUpdateExpirationTime;1290 }1291 if (childChildExpirationTime > newChildExpirationTime) {1292 newChildExpirationTime = childChildExpirationTime;1293 }1294 child = child.sibling;1295 }1296 }1297 completedWork.childExpirationTime = newChildExpirationTime;1298}1299function commitRoot(root) {1300 runWithPriority(ImmediatePriority, commitRootImpl.bind(null, root));1301 // If there are passive effects, schedule a callback to flush them. This goes1302 // outside commitRootImpl so that it inherits the priority of the render.1303 // å¦æææ¶æå½±åï¼å®æä¸ä¸ªåè°æ¥æ¸
é¤å®ä»¬ã1304 // è¿è¶
åºäºcommitRootImplï¼å æ¤å®ç»§æ¿äºåç°çä¼å
级ã1305 if (rootWithPendingPassiveEffects !== null) {1306 const priorityLevel = getCurrentPriorityLevel();1307 scheduleCallback(priorityLevel, () => {1308 flushPassiveEffects();1309 return null;1310 });1311 }1312 return null;1313}1314function commitRootImpl(root) {1315 flushPassiveEffects();1316 flushRenderPhaseStrictModeWarningsInDEV();1317 flushSuspensePriorityWarningInDEV();1318 invariant(1319 workPhase !== RenderPhase && workPhase !== CommitPhase,1320 'Should not already be working.',1321 );1322 const finishedWork = root.finishedWork;1323 const expirationTime = root.finishedExpirationTime;1324 if (finishedWork === null) {1325 return null;1326 }1327 root.finishedWork = null;1328 root.finishedExpirationTime = NoWork;1329 invariant(1330 finishedWork !== root.current,1331 'Cannot commit the same tree as before. This error is likely caused by ' +1332 'a bug in React. Please file an issue.',1333 );1334 // commitRoot never returns a continuation; it always finishes synchronously.1335 // So we can clear these now to allow a new callback to be scheduled.1336 // commitRoot ä»ä¸è¿å延ç»;å®æ»æ¯åæ¥å®æã1337 // å æ¤æ们ç°å¨å¯ä»¥æ¸
é¤è¿äºï¼ä»¥ä¾¿å®æä¸ä¸ªæ°çåè°ã1338 root.callbackNode = null;1339 root.callbackExpirationTime = NoWork;1340 startCommitTimer();1341 // Update the first and last pending times on this root. The new first1342 // pending time is whatever is left on the root fiber.1343 // æ´æ°è¿ä¸ªæ ¹ç®å½ä¸ç第ä¸ä¸ªåæåä¸ä¸ªæèµ·æ¶é´ã1344 // æ°ç第ä¸ä¸ªæèµ·æ¶é´æ¯çå¨æ ¹ fiber ä¸çæ¶é´ã1345 const updateExpirationTimeBeforeCommit = finishedWork.expirationTime;1346 const childExpirationTimeBeforeCommit = finishedWork.childExpirationTime;1347 const firstPendingTimeBeforeCommit =1348 childExpirationTimeBeforeCommit > updateExpirationTimeBeforeCommit1349 ? childExpirationTimeBeforeCommit1350 : updateExpirationTimeBeforeCommit;1351 root.firstPendingTime = firstPendingTimeBeforeCommit;1352 if (firstPendingTimeBeforeCommit < root.lastPendingTime) {1353 // This usually means we've finished all the work, but it can also happen1354 // when something gets downprioritized during render, like a hidden tree.1355 // è¿é常æå³çæ们已ç»å®æäºææçå·¥ä½ï¼ä½å®ä¹å¯è½åçå¨æ¸²æè¿ç¨ä¸ä¸äºäºæ
çä¼å
级éä½ï¼æ¯å¦éèçæ ã1356 root.lastPendingTime = firstPendingTimeBeforeCommit;1357 }1358 if (root === workInProgressRoot) {1359 // We can reset these now that they are finished.1360 // ç°å¨è¿äºé½å®æäºï¼æ们å¯ä»¥éæ°è®¾ç½®ã1361 workInProgressRoot = null;1362 workInProgress = null;1363 renderExpirationTime = NoWork;1364 } else {1365 // This indicates that the last root we worked on is not the same one that1366 // we're committing now. This most commonly happens when a suspended root1367 // times out.1368 // è¿è¡¨ææ们å¤ççæåä¸ä¸ªæ ¹ä¸æ¯æ们ç°å¨æ交çæ ¹ã1369 // è¿é常åçå¨æèµ·çæ ¹è¶
æ¶æ¶ã1370 }1371 // Get the list of effects.1372 // è·åææå表ã1373 let firstEffect;1374 if (finishedWork.effectTag > PerformedWork) {1375 // A fiber's effect list consists only of its children, not itself. So if1376 // the root has an effect, we need to add it to the end of the list. The1377 // resulting list is the set that would belong to the root's parent, if it1378 // had one; that is, all the effects in the tree including the root.1379 // fiber çææå表åªå
æ¬å®çåèç¹ï¼èä¸æ¯å®æ¬èº«ã1380 // å æ¤ï¼å¦ææ ¹æææï¼æ们éè¦å°å®æ·»å å°å表çæ«å°¾ã1381 // ç»æå表æ¯æ ¹çç¶å表çéåï¼å¦ææ ¹æç¶å表çè¯;1382 // ä¹å°±æ¯è¯´ï¼æ ä¸çææææï¼å
æ¬æ ¹ã1383 if (finishedWork.lastEffect !== null) {1384 finishedWork.lastEffect.nextEffect = finishedWork;1385 firstEffect = finishedWork.firstEffect;1386 } else {1387 firstEffect = finishedWork;1388 }1389 } else {1390 // There is no effect on the root.1391 // å¯¹æ ¹æ²¡æå½±åã1392 firstEffect = finishedWork.firstEffect;1393 }1394 if (firstEffect !== null) {1395 const prevWorkPhase = workPhase;1396 workPhase = CommitPhase;1397 let prevInteractions: Set<Interaction> | null = null;1398 if (enableSchedulerTracing) {1399 prevInteractions = __interactionsRef.current;1400 __interactionsRef.current = root.memoizedInteractions;1401 }1402 // Reset this to null before calling lifecycles1403 // å¨è°ç¨çå½å¨æä¹åå°å
¶é置为null1404 ReactCurrentOwner.current = null;1405 // The commit phase is broken into several sub-phases. We do a separate pass1406 // of the effect list for each phase: all mutation effects come before all1407 // layout effects, and so on.1408 // The first phase a "before mutation" phase. We use this phase to read the1409 // state of the host tree right before we mutate it. This is where1410 // getSnapshotBeforeUpdate is called.1411 // æ交é¶æ®µå为å 个åé¶æ®µã1412 // æ们对æ¯ä¸ªé¶æ®µçææå表è¿è¡åç¬çéå:ææççªåææé½å¨ææå¸å±ææä¹åï¼ççã1413 // 第ä¸é¶æ®µæ¯âçªååâé¶æ®µã1414 // å¨å¯¹ä¸»æºæ è¿è¡çªåä¹åï¼æ们使ç¨è¿ä¸ªé¶æ®µæ¥è¯»å主æºæ çç¶æã1415 // è¿å°±æ¯è°ç¨getSnapshotBeforeUpdateçå°æ¹ã1416 startCommitSnapshotEffectsTimer();1417 prepareForCommit(root.containerInfo);1418 nextEffect = firstEffect;1419 do {1420 if (__DEV__) {1421 invokeGuardedCallback(null, commitBeforeMutationEffects, null);1422 if (hasCaughtError()) {1423 invariant(nextEffect !== null, 'Should be working on an effect.');1424 const error = clearCaughtError();1425 captureCommitPhaseError(nextEffect, error);1426 nextEffect = nextEffect.nextEffect;1427 }1428 } else {1429 try {1430 commitBeforeMutationEffects();1431 } catch (error) {1432 invariant(nextEffect !== null, 'Should be working on an effect.');1433 captureCommitPhaseError(nextEffect, error);1434 nextEffect = nextEffect.nextEffect;1435 }1436 }1437 } while (nextEffect !== null);1438 stopCommitSnapshotEffectsTimer();1439 if (enableProfilerTimer) {1440 // Mark the current commit time to be shared by all Profilers in this1441 // batch. This enables them to be grouped later.1442 recordCommitTime();1443 }1444 // The next phase is the mutation phase, where we mutate the host tree.1445 // ä¸ä¸ä¸ªé¶æ®µæ¯çªåé¶æ®µï¼æ们对宿主æ è¿è¡çªåã1446 startCommitHostEffectsTimer();1447 nextEffect = firstEffect;1448 do {1449 if (__DEV__) {1450 invokeGuardedCallback(null, commitMutationEffects, null);1451 if (hasCaughtError()) {1452 invariant(nextEffect !== null, 'Should be working on an effect.');1453 const error = clearCaughtError();1454 captureCommitPhaseError(nextEffect, error);1455 nextEffect = nextEffect.nextEffect;1456 }1457 } else {1458 try {1459 commitMutationEffects();1460 } catch (error) {1461 invariant(nextEffect !== null, 'Should be working on an effect.');1462 captureCommitPhaseError(nextEffect, error);1463 nextEffect = nextEffect.nextEffect;1464 }1465 }1466 } while (nextEffect !== null);1467 stopCommitHostEffectsTimer();1468 resetAfterCommit(root.containerInfo);1469 // The work-in-progress tree is now the current tree. This must come after1470 // the mutation phase, so that the previous tree is still current during1471 // componentWillUnmount, but before the layout phase, so that the finished1472 // work is current during componentDidMount/Update.1473 // æ£å¨è¿è¡çå·¥ä½æ ç°å¨æ¯å½åæ ã1474 // è¿å¿
é¡»å¨çªåé¶æ®µä¹åè¿è¡ï¼ä»¥ä¾¿å
åçæ å¨componentWillUnmountæé´ä»ç¶æ¯å½åçï¼1475 // ä½æ¯å¨å¸å±é¶æ®µä¹åï¼ä»¥ä¾¿å®æçå·¥ä½å¨componentDidMount/Updateæé´æ¯å½åçã1476 root.current = finishedWork;1477 // The next phase is the layout phase, where we call effects that read1478 // the host tree after it's been mutated. The idiomatic use case for this is1479 // layout, but class component lifecycles also fire here for legacy reasons.1480 // ä¸ä¸ä¸ªé¶æ®µæ¯å¸å±é¶æ®µï¼å¨è¿éæ们è°ç¨å¨ä¸»æºæ åççªåå读åå®çææã1481 // è¿éçæ¯ç¨ç¨ä¾æ¯layoutï¼ä½æ¯ç±»ç»ä»¶ççå½å¨æä¹ä¼å 为éçé®é¢è触åã1482 startCommitLifeCyclesTimer();1483 nextEffect = firstEffect;1484 do {1485 if (__DEV__) {1486 invokeGuardedCallback(1487 null,1488 commitLayoutEffects,1489 null,1490 root,1491 expirationTime,1492 );1493 if (hasCaughtError()) {1494 invariant(nextEffect !== null, 'Should be working on an effect.');1495 const error = clearCaughtError();1496 captureCommitPhaseError(nextEffect, error);1497 nextEffect = nextEffect.nextEffect;1498 }1499 } else {1500 try {1501 commitLayoutEffects(root, expirationTime);1502 } catch (error) {1503 invariant(nextEffect !== null, 'Should be working on an effect.');1504 captureCommitPhaseError(nextEffect, error);1505 nextEffect = nextEffect.nextEffect;1506 }1507 }1508 } while (nextEffect !== null);1509 stopCommitLifeCyclesTimer();1510 nextEffect = null;1511 if (enableSchedulerTracing) {1512 __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);1513 }1514 workPhase = prevWorkPhase;1515 } else {1516 // No effects.1517 root.current = finishedWork;1518 // Measure these anyway so the flamegraph explicitly shows that there were1519 // no effects.1520 // TODO: Maybe there's a better way to report this.1521 startCommitSnapshotEffectsTimer();1522 stopCommitSnapshotEffectsTimer();1523 if (enableProfilerTimer) {1524 recordCommitTime();1525 }1526 startCommitHostEffectsTimer();1527 stopCommitHostEffectsTimer();1528 startCommitLifeCyclesTimer();1529 stopCommitLifeCyclesTimer();1530 }1531 stopCommitTimer();1532 if (rootDoesHavePassiveEffects) {1533 // This commit has passive effects. Stash a reference to them. But don't1534 // schedule a callback until after flushing layout work.1535 // è¿ä¸ª commit ææ¶æå½±åãåå¨ä¸ä¸ªå¼ç¨æåå®ãä½æ¯å¨å·æ°å¸å±å·¥ä½ä¹åä¸è¦å®æåè°ã1536 rootDoesHavePassiveEffects = false;1537 rootWithPendingPassiveEffects = root;1538 pendingPassiveEffectsExpirationTime = expirationTime;1539 } else {1540 if (enableSchedulerTracing) {1541 // If there are no passive effects, then we can complete the pending1542 // interactions. Otherwise, we'll wait until after the passive effects1543 // are flushed.1544 finishPendingInteractions(root, expirationTime);1545 }1546 }1547 // Check if there's remaining work on this root1548 // æ£æ¥è¿ä¸ªæ ¹ä¸æ¯å¦è¿æå©ä½çå·¥ä½1549 const remainingExpirationTime = root.firstPendingTime;1550 if (remainingExpirationTime !== NoWork) {1551 const currentTime = requestCurrentTime();1552 const priorityLevel = inferPriorityFromExpirationTime(1553 currentTime,1554 remainingExpirationTime,1555 );1556 scheduleCallbackForRoot(root, priorityLevel, remainingExpirationTime);1557 } else {1558 // If there's no remaining work, we can clear the set of already failed1559 // error boundaries.1560 // å¦æ没æå©ä½çå·¥ä½ï¼æ们å¯ä»¥æ¸
é¤ä¸ç»å·²ç»å¤±è´¥çé误边çã1561 legacyErrorBoundariesThatAlreadyFailed = null;1562 }1563 onCommitRoot(finishedWork.stateNode, expirationTime);1564 if (remainingExpirationTime === Sync) {1565 // Count the number of times the root synchronously re-renders without1566 // finishing. If there are too many, it indicates an infinite update loop.1567 // 计ç®æ ¹èç¹æªå®æåæ¥éæ°åç°ç次æ°ãå¦ææ°é太å¤ï¼å表示æ éæ´æ°å¾ªç¯ã1568 if (root === rootWithNestedUpdates) {1569 nestedUpdateCount++;1570 } else {1571 nestedUpdateCount = 0;1572 rootWithNestedUpdates = root;1573 }1574 } else {1575 nestedUpdateCount = 0;1576 }1577 if (hasUncaughtError) {1578 hasUncaughtError = false;1579 const error = firstUncaughtError;1580 firstUncaughtError = null;1581 throw error;1582 }1583 if (workPhase === LegacyUnbatchedPhase) {1584 // This is a legacy edge case. We just committed the initial mount of1585 // a ReactDOM.render-ed root inside of batchedUpdates. The commit fired1586 // synchronously, but layout updates should be deferred until the end1587 // of the batch.1588 // è¿æ¯ä¸ä¸ªéççè¾¹ç¼æ¡ä¾ã1589 // æ们åªæ¯æäº¤äº ReactDOM.render-ed root çåå§æè½½å¨ batchedUpdatesã1590 // æ交æ¯åæ¥è§¦åçï¼ä½æ¯å¸å±æ´æ°åºè¯¥å»¶è¿å°æ¹å¤çç»ææ¶ã1591 return null;1592 }1593 // If layout work was scheduled, flush it now.1594 // å¦æå¸å±å·¥ä½å·²ç»å®æ好äºï¼ç°å¨å°±å·æ°å®ã1595 flushSyncCallbackQueue();1596 return null;1597}1598function commitBeforeMutationEffects() {1599 while (nextEffect !== null) {1600 if ((nextEffect.effectTag & Snapshot) !== NoEffect) {1601 setCurrentDebugFiberInDEV(nextEffect);1602 recordEffect();1603 const current = nextEffect.alternate;1604 commitBeforeMutationEffectOnFiber(current, nextEffect);1605 resetCurrentDebugFiberInDEV();1606 }1607 nextEffect = nextEffect.nextEffect;1608 }1609}1610function commitMutationEffects() {1611 // TODO: Should probably move the bulk of this function to commitWork.1612 while (nextEffect !== null) {1613 setCurrentDebugFiberInDEV(nextEffect);1614 const effectTag = nextEffect.effectTag;1615 if (effectTag & ContentReset) {1616 commitResetTextContent(nextEffect);1617 }1618 if (effectTag & Ref) {1619 const current = nextEffect.alternate;1620 if (current !== null) {1621 commitDetachRef(current);1622 }1623 }1624 // The following switch statement is only concerned about placement,1625 // updates, and deletions. To avoid needing to add a case for every possible1626 // bitmap value, we remove the secondary effects from the effect tag and1627 // switch on that value.1628 // ä¸é¢çswitchè¯å¥åªå
³å¿æ¾ç½®ãæ´æ°åå é¤ã1629 // 为äºé¿å
为æ¯ä¸ªå¯è½çä½å¾å¼æ·»å 大å°åï¼æ们ä»effectæ ç¾ä¸å é¤æ¬¡è¦çææï¼å¹¶åæ¢å°è¯¥å¼ã1630 let primaryEffectTag = effectTag & (Placement | Update | Deletion);1631 switch (primaryEffectTag) {1632 case Placement: {1633 commitPlacement(nextEffect);1634 // Clear the "placement" from effect tag so that we know that this is1635 // inserted, before any life-cycles like componentDidMount gets called.1636 // æ¸
é¤effectæ ç¾ä¸çâæ¾ç½®âï¼è¿æ ·æ们就ç¥éå®å·²ç»è¢«æå
¥äºï¼å¨è°ç¨componentDidMountè¿æ ·ççå½å¨æä¹åã1637 // TODO: findDOMNode doesn't rely on this any more but isMounted does1638 // and isMounted is deprecated anyway so we should be able to kill this.1639 nextEffect.effectTag &= ~Placement;1640 break;1641 }1642 case PlacementAndUpdate: {1643 // Placement1644 commitPlacement(nextEffect);1645 // Clear the "placement" from effect tag so that we know that this is1646 // inserted, before any life-cycles like componentDidMount gets called.1647 // æ¸
é¤effectæ ç¾ä¸çâæ¾ç½®âï¼è¿æ ·æ们就ç¥éå®å·²ç»è¢«æå
¥äºï¼å¨è°ç¨componentDidMountè¿æ ·ççå½å¨æä¹åã1648 nextEffect.effectTag &= ~Placement;1649 // Update1650 const current = nextEffect.alternate;1651 commitWork(current, nextEffect);1652 break;1653 }1654 case Update: {1655 const current = nextEffect.alternate;1656 commitWork(current, nextEffect);1657 break;1658 }1659 case Deletion: {1660 commitDeletion(nextEffect);1661 break;1662 }1663 }1664 // TODO: Only record a mutation effect if primaryEffectTag is non-zero.1665 recordEffect();1666 resetCurrentDebugFiberInDEV();1667 nextEffect = nextEffect.nextEffect;1668 }1669}1670function commitLayoutEffects(1671 root: FiberRoot,1672 committedExpirationTime: ExpirationTime,1673) {1674 // TODO: Should probably move the bulk of this function to commitWork.1675 while (nextEffect !== null) {1676 setCurrentDebugFiberInDEV(nextEffect);1677 const effectTag = nextEffect.effectTag;1678 if (effectTag & (Update | Callback)) {1679 recordEffect();1680 const current = nextEffect.alternate;1681 commitLayoutEffectOnFiber(1682 root,1683 current,1684 nextEffect,1685 committedExpirationTime,1686 );1687 }1688 if (effectTag & Ref) {1689 recordEffect();1690 commitAttachRef(nextEffect);1691 }1692 if (effectTag & Passive) {1693 rootDoesHavePassiveEffects = true;1694 }1695 resetCurrentDebugFiberInDEV();1696 nextEffect = nextEffect.nextEffect;1697 }1698}1699export function flushPassiveEffects() {1700 if (rootWithPendingPassiveEffects === null) {1701 return false;1702 }1703 const root = rootWithPendingPassiveEffects;1704 const expirationTime = pendingPassiveEffectsExpirationTime;1705 rootWithPendingPassiveEffects = null;1706 pendingPassiveEffectsExpirationTime = NoWork;1707 let prevInteractions: Set<Interaction> | null = null;1708 if (enableSchedulerTracing) {1709 prevInteractions = __interactionsRef.current;1710 __interactionsRef.current = root.memoizedInteractions;1711 }1712 invariant(1713 workPhase !== RenderPhase && workPhase !== CommitPhase,1714 'Cannot flush passive effects while already rendering.',1715 );1716 const prevWorkPhase = workPhase;1717 workPhase = CommitPhase;1718 // Note: This currently assumes there are no passive effects on the root1719 // fiber, because the root is not part of its own effect list. This could1720 // change in the future.1721 let effect = root.current.firstEffect;1722 while (effect !== null) {1723 if (__DEV__) {1724 setCurrentDebugFiberInDEV(effect);1725 invokeGuardedCallback(null, commitPassiveHookEffects, null, effect);1726 if (hasCaughtError()) {1727 invariant(effect !== null, 'Should be working on an effect.');1728 const error = clearCaughtError();1729 captureCommitPhaseError(effect, error);1730 }1731 resetCurrentDebugFiberInDEV();1732 } else {1733 try {1734 commitPassiveHookEffects(effect);1735 } catch (error) {1736 invariant(effect !== null, 'Should be working on an effect.');1737 captureCommitPhaseError(effect, error);1738 }1739 }1740 effect = effect.nextEffect;1741 }1742 if (enableSchedulerTracing) {1743 __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);1744 finishPendingInteractions(root, expirationTime);1745 }1746 workPhase = prevWorkPhase;1747 flushSyncCallbackQueue();1748 // If additional passive effects were scheduled, increment a counter. If this1749 // exceeds the limit, we'll fire a warning.1750 nestedPassiveUpdateCount =1751 rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;1752 return true;1753}1754export function isAlreadyFailedLegacyErrorBoundary(instance: mixed): boolean {1755 return (1756 legacyErrorBoundariesThatAlreadyFailed !== null &&1757 legacyErrorBoundariesThatAlreadyFailed.has(instance)1758 );1759}1760export function markLegacyErrorBoundaryAsFailed(instance: mixed) {1761 if (legacyErrorBoundariesThatAlreadyFailed === null) {1762 legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);1763 } else {1764 legacyErrorBoundariesThatAlreadyFailed.add(instance);1765 }1766}1767function prepareToThrowUncaughtError(error: mixed) {1768 if (!hasUncaughtError) {1769 hasUncaughtError = true;1770 firstUncaughtError = error;1771 }1772}1773export const onUncaughtError = prepareToThrowUncaughtError;1774function captureCommitPhaseErrorOnRoot(1775 rootFiber: Fiber,1776 sourceFiber: Fiber,1777 error: mixed,1778) {1779 const errorInfo = createCapturedValue(error, sourceFiber);1780 const update = createRootErrorUpdate(rootFiber, errorInfo, Sync);1781 enqueueUpdate(rootFiber, update);1782 const root = markUpdateTimeFromFiberToRoot(rootFiber, Sync);1783 if (root !== null) {1784 scheduleCallbackForRoot(root, ImmediatePriority, Sync);1785 }1786}1787export function captureCommitPhaseError(sourceFiber: Fiber, error: mixed) {1788 if (sourceFiber.tag === HostRoot) {1789 // Error was thrown at the root. There is no parent, so the root1790 // itself should capture it.1791 captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);1792 return;1793 }1794 let fiber = sourceFiber.return;1795 while (fiber !== null) {1796 if (fiber.tag === HostRoot) {1797 captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);1798 return;1799 } else if (fiber.tag === ClassComponent) {1800 const ctor = fiber.type;1801 const instance = fiber.stateNode;1802 if (1803 typeof ctor.getDerivedStateFromError === 'function' ||1804 (typeof instance.componentDidCatch === 'function' &&1805 !isAlreadyFailedLegacyErrorBoundary(instance))1806 ) {1807 const errorInfo = createCapturedValue(error, sourceFiber);1808 const update = createClassErrorUpdate(1809 fiber,1810 errorInfo,1811 // TODO: This is always sync1812 Sync,1813 );1814 enqueueUpdate(fiber, update);1815 const root = markUpdateTimeFromFiberToRoot(fiber, Sync);1816 if (root !== null) {1817 scheduleCallbackForRoot(root, ImmediatePriority, Sync);1818 }1819 return;1820 }1821 }1822 fiber = fiber.return;1823 }1824}1825export function pingSuspendedRoot(1826 root: FiberRoot,1827 thenable: Thenable,1828 suspendedTime: ExpirationTime,1829) {1830 const pingCache = root.pingCache;1831 if (pingCache !== null) {1832 // The thenable resolved, so we no longer need to memoize, because it will1833 // never be thrown again.1834 pingCache.delete(thenable);1835 }1836 if (workInProgressRoot === root && renderExpirationTime === suspendedTime) {1837 // Received a ping at the same priority level at which we're currently1838 // rendering. Restart from the root. Don't need to schedule a ping because1839 // we're already working on this tree.1840 prepareFreshStack(root, renderExpirationTime);1841 return;1842 }1843 const lastPendingTime = root.lastPendingTime;1844 if (lastPendingTime < suspendedTime) {1845 // The root is no longer suspended at this time.1846 return;1847 }1848 const pingTime = root.pingTime;1849 if (pingTime !== NoWork && pingTime < suspendedTime) {1850 // There's already a lower priority ping scheduled.1851 return;1852 }1853 // Mark the time at which this ping was scheduled.1854 root.pingTime = suspendedTime;1855 if (root.finishedExpirationTime === suspendedTime) {1856 // If there's a pending fallback waiting to commit, throw it away.1857 root.finishedExpirationTime = NoWork;1858 root.finishedWork = null;1859 }1860 const currentTime = requestCurrentTime();1861 const priorityLevel = inferPriorityFromExpirationTime(1862 currentTime,1863 suspendedTime,1864 );1865 scheduleCallbackForRoot(root, priorityLevel, suspendedTime);1866}1867export function retryTimedOutBoundary(boundaryFiber: Fiber) {1868 // The boundary fiber (a Suspense component) previously timed out and was1869 // rendered in its fallback state. One of the promises that suspended it has1870 // resolved, which means at least part of the tree was likely unblocked. Try1871 // rendering again, at a new expiration time.1872 const currentTime = requestCurrentTime();1873 const suspenseConfig = null; // Retries don't carry over the already committed update.1874 const retryTime = computeExpirationForFiber(1875 currentTime,1876 boundaryFiber,1877 suspenseConfig,1878 );1879 // TODO: Special case idle priority?1880 const priorityLevel = inferPriorityFromExpirationTime(currentTime, retryTime);1881 const root = markUpdateTimeFromFiberToRoot(boundaryFiber, retryTime);1882 if (root !== null) {1883 scheduleCallbackForRoot(root, priorityLevel, retryTime);1884 }1885}1886export function resolveRetryThenable(boundaryFiber: Fiber, thenable: Thenable) {1887 let retryCache: WeakSet<Thenable> | Set<Thenable> | null;1888 if (enableSuspenseServerRenderer) {1889 switch (boundaryFiber.tag) {1890 case SuspenseComponent:1891 retryCache = boundaryFiber.stateNode;1892 break;1893 case DehydratedSuspenseComponent:1894 retryCache = boundaryFiber.memoizedState;1895 break;1896 default:1897 invariant(1898 false,1899 'Pinged unknown suspense boundary type. ' +1900 'This is probably a bug in React.',1901 );1902 }1903 } else {1904 retryCache = boundaryFiber.stateNode;1905 }1906 if (retryCache !== null) {1907 // The thenable resolved, so we no longer need to memoize, because it will1908 // never be thrown again.1909 retryCache.delete(thenable);1910 }1911 retryTimedOutBoundary(boundaryFiber);1912}1913// Computes the next Just Noticeable Difference (JND) boundary.1914// The theory is that a person can't tell the difference between small differences in time.1915// Therefore, if we wait a bit longer than necessary that won't translate to a noticeable1916// difference in the experience. However, waiting for longer might mean that we can avoid1917// showing an intermediate loading state. The longer we have already waited, the harder it1918// is to tell small differences in time. Therefore, the longer we've already waited,1919// the longer we can wait additionally. At some point we have to give up though.1920// We pick a train model where the next boundary commits at a consistent schedule.1921// These particular numbers are vague estimates. We expect to adjust them based on research.1922function jnd(timeElapsed: number) {1923 return timeElapsed < 1201924 ? 1201925 : timeElapsed < 4801926 ? 4801927 : timeElapsed < 10801928 ? 10801929 : timeElapsed < 19201930 ? 19201931 : timeElapsed < 30001932 ? 30001933 : timeElapsed < 43201934 ? 43201935 : ceil(timeElapsed / 1960) * 1960;1936}1937function computeMsUntilSuspenseLoadingDelay(1938 mostRecentEventTime: ExpirationTime,1939 committedExpirationTime: ExpirationTime,1940 suspenseConfig: SuspenseConfig,1941) {1942 if (disableYielding) {1943 // Timeout immediately when yielding is disabled.1944 return 0;1945 }1946 const busyMinDurationMs = (suspenseConfig.busyMinDurationMs: any) | 0;1947 if (busyMinDurationMs <= 0) {1948 return 0;1949 }1950 const busyDelayMs = (suspenseConfig.busyDelayMs: any) | 0;1951 // Compute the time until this render pass would expire.1952 // 计ç®è¿ä¸ªæ¸²æééè¿æçæ¶é´ã1953 const currentTimeMs: number = now();1954 const eventTimeMs: number = inferTimeFromExpirationTime(1955 mostRecentEventTime,1956 suspenseConfig,1957 );1958 const timeElapsed = currentTimeMs - eventTimeMs;1959 if (timeElapsed <= busyDelayMs) {1960 // If we haven't yet waited longer than the initial delay, we don't1961 // have to wait any additional time.1962 // å¦ææ们ççå¾
æ¶é´è¿æ²¡æè¶
è¿åå®ç延误æ¶é´ï¼æ们就ä¸å¿
åçäºã1963 return 0;1964 }1965 const msUntilTimeout = busyDelayMs + busyMinDurationMs - timeElapsed;1966 // This is the value that is passed to `setTimeout`.1967 return msUntilTimeout;1968}1969function computeMsUntilTimeout(1970 mostRecentEventTime: ExpirationTime,1971 suspenseTimeout: ExpirationTime,1972 committedExpirationTime: ExpirationTime,1973 suspenseConfig: null | SuspenseConfig,1974 shouldDelay: boolean,1975) {1976 if (disableYielding) {1977 // Timeout immediately when yielding is disabled.1978 return 0;1979 }1980 // Compute the time until this render pass would expire.1981 const currentTimeMs: number = now();1982 if (suspenseTimeout !== Sync && shouldDelay) {1983 const timeUntilTimeoutMs =1984 expirationTimeToMs(suspenseTimeout) - currentTimeMs;1985 return timeUntilTimeoutMs;1986 }1987 const eventTimeMs: number = inferTimeFromExpirationTime(1988 mostRecentEventTime,1989 suspenseConfig,1990 );1991 const timeUntilExpirationMs =1992 expirationTimeToMs(committedExpirationTime) - currentTimeMs;1993 let timeElapsed = currentTimeMs - eventTimeMs;1994 if (timeElapsed < 0) {1995 // We get this wrong some time since we estimate the time.1996 timeElapsed = 0;1997 }1998 let msUntilTimeout = jnd(timeElapsed) - timeElapsed;1999 // Clamp the timeout to the expiration time.2000 // TODO: Once the event time is exact instead of inferred from expiration time2001 // we don't need this....
ReactFiberScheduler.js
Source:ReactFiberScheduler.js
...871 ) {872 workInProgressRootExitStatus = RootErrored;873 }874}875function inferTimeFromExpirationTime(expirationTime: ExpirationTime): number {876 // We don't know exactly when the update was scheduled, but we can infer an877 // approximate start time from the expiration time.878 const earliestExpirationTimeMs = expirationTimeToMs(expirationTime);879 return earliestExpirationTimeMs - LOW_PRIORITY_EXPIRATION;880}881function workLoopSync() {882 // Already timed out, so perform work without checking if we need to yield.883 while (workInProgress !== null) {884 workInProgress = performUnitOfWork(workInProgress);885 }886}887function workLoop() {888 // Perform work until Scheduler asks us to yield889 while (workInProgress !== null && !shouldYield()) {890 workInProgress = performUnitOfWork(workInProgress);891 }892}893function performUnitOfWork(unitOfWork: Fiber): Fiber | null {894 // The current, flushed, state of this fiber is the alternate. Ideally895 // nothing should rely on this, but relying on it here means that we don't896 // need an additional field on the work in progress.897 const current = unitOfWork.alternate;898 startWorkTimer(unitOfWork);899 setCurrentDebugFiberInDEV(unitOfWork);900 let next;901 if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoContext) {902 startProfilerTimer(unitOfWork);903 next = beginWork(current, unitOfWork, renderExpirationTime);904 stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);905 } else {906 next = beginWork(current, unitOfWork, renderExpirationTime);907 }908 resetCurrentDebugFiberInDEV();909 unitOfWork.memoizedProps = unitOfWork.pendingProps;910 if (next === null) {911 // If this doesn't spawn new work, complete the current work.912 next = completeUnitOfWork(unitOfWork);913 }914 ReactCurrentOwner.current = null;915 return next;916}917function completeUnitOfWork(unitOfWork: Fiber): Fiber | null {918 // Attempt to complete the current unit of work, then move to the next919 // sibling. If there are no more siblings, return to the parent fiber.920 workInProgress = unitOfWork;921 do {922 // The current, flushed, state of this fiber is the alternate. Ideally923 // nothing should rely on this, but relying on it here means that we don't924 // need an additional field on the work in progress.925 const current = workInProgress.alternate;926 const returnFiber = workInProgress.return;927 // Check if the work completed or if something threw.928 if ((workInProgress.effectTag & Incomplete) === NoEffect) {929 setCurrentDebugFiberInDEV(workInProgress);930 let next;931 if (932 !enableProfilerTimer ||933 (workInProgress.mode & ProfileMode) === NoContext934 ) {935 next = completeWork(current, workInProgress, renderExpirationTime);936 } else {937 startProfilerTimer(workInProgress);938 next = completeWork(current, workInProgress, renderExpirationTime);939 // Update render duration assuming we didn't error.940 stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);941 }942 stopWorkTimer(workInProgress);943 resetCurrentDebugFiberInDEV();944 resetChildExpirationTime(workInProgress);945 if (next !== null) {946 // Completing this fiber spawned new work. Work on that next.947 return next;948 }949 if (950 returnFiber !== null &&951 // Do not append effects to parents if a sibling failed to complete952 (returnFiber.effectTag & Incomplete) === NoEffect953 ) {954 // Append all the effects of the subtree and this fiber onto the effect955 // list of the parent. The completion order of the children affects the956 // side-effect order.957 if (returnFiber.firstEffect === null) {958 returnFiber.firstEffect = workInProgress.firstEffect;959 }960 if (workInProgress.lastEffect !== null) {961 if (returnFiber.lastEffect !== null) {962 returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;963 }964 returnFiber.lastEffect = workInProgress.lastEffect;965 }966 // If this fiber had side-effects, we append it AFTER the children's967 // side-effects. We can perform certain side-effects earlier if needed,968 // by doing multiple passes over the effect list. We don't want to969 // schedule our own side-effect on our own list because if end up970 // reusing children we'll schedule this effect onto itself since we're971 // at the end.972 const effectTag = workInProgress.effectTag;973 // Skip both NoWork and PerformedWork tags when creating the effect974 // list. PerformedWork effect is read by React DevTools but shouldn't be975 // committed.976 if (effectTag > PerformedWork) {977 if (returnFiber.lastEffect !== null) {978 returnFiber.lastEffect.nextEffect = workInProgress;979 } else {980 returnFiber.firstEffect = workInProgress;981 }982 returnFiber.lastEffect = workInProgress;983 }984 }985 } else {986 // This fiber did not complete because something threw. Pop values off987 // the stack without entering the complete phase. If this is a boundary,988 // capture values if possible.989 const next = unwindWork(workInProgress, renderExpirationTime);990 // Because this fiber did not complete, don't reset its expiration time.991 if (992 enableProfilerTimer &&993 (workInProgress.mode & ProfileMode) !== NoContext994 ) {995 // Record the render duration for the fiber that errored.996 stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);997 // Include the time spent working on failed children before continuing.998 let actualDuration = workInProgress.actualDuration;999 let child = workInProgress.child;1000 while (child !== null) {1001 actualDuration += child.actualDuration;1002 child = child.sibling;1003 }1004 workInProgress.actualDuration = actualDuration;1005 }1006 if (next !== null) {1007 // If completing this work spawned new work, do that next. We'll come1008 // back here again.1009 // Since we're restarting, remove anything that is not a host effect1010 // from the effect tag.1011 // TODO: The name stopFailedWorkTimer is misleading because Suspense1012 // also captures and restarts.1013 stopFailedWorkTimer(workInProgress);1014 next.effectTag &= HostEffectMask;1015 return next;1016 }1017 stopWorkTimer(workInProgress);1018 if (returnFiber !== null) {1019 // Mark the parent fiber as incomplete and clear its effect list.1020 returnFiber.firstEffect = returnFiber.lastEffect = null;1021 returnFiber.effectTag |= Incomplete;1022 }1023 }1024 const siblingFiber = workInProgress.sibling;1025 if (siblingFiber !== null) {1026 // If there is more work to do in this returnFiber, do that next.1027 return siblingFiber;1028 }1029 // Otherwise, return to the parent1030 workInProgress = returnFiber;1031 } while (workInProgress !== null);1032 // We've reached the root.1033 if (workInProgressRootExitStatus === RootIncomplete) {1034 workInProgressRootExitStatus = RootCompleted;1035 }1036 return null;1037}1038function resetChildExpirationTime(completedWork: Fiber) {1039 if (1040 renderExpirationTime !== Never &&1041 completedWork.childExpirationTime === Never1042 ) {1043 // The children of this component are hidden. Don't bubble their1044 // expiration times.1045 return;1046 }1047 let newChildExpirationTime = NoWork;1048 // Bubble up the earliest expiration time.1049 if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoContext) {1050 // In profiling mode, resetChildExpirationTime is also used to reset1051 // profiler durations.1052 let actualDuration = completedWork.actualDuration;1053 let treeBaseDuration = completedWork.selfBaseDuration;1054 // When a fiber is cloned, its actualDuration is reset to 0. This value will1055 // only be updated if work is done on the fiber (i.e. it doesn't bailout).1056 // When work is done, it should bubble to the parent's actualDuration. If1057 // the fiber has not been cloned though, (meaning no work was done), then1058 // this value will reflect the amount of time spent working on a previous1059 // render. In that case it should not bubble. We determine whether it was1060 // cloned by comparing the child pointer.1061 const shouldBubbleActualDurations =1062 completedWork.alternate === null ||1063 completedWork.child !== completedWork.alternate.child;1064 let child = completedWork.child;1065 while (child !== null) {1066 const childUpdateExpirationTime = child.expirationTime;1067 const childChildExpirationTime = child.childExpirationTime;1068 if (childUpdateExpirationTime > newChildExpirationTime) {1069 newChildExpirationTime = childUpdateExpirationTime;1070 }1071 if (childChildExpirationTime > newChildExpirationTime) {1072 newChildExpirationTime = childChildExpirationTime;1073 }1074 if (shouldBubbleActualDurations) {1075 actualDuration += child.actualDuration;1076 }1077 treeBaseDuration += child.treeBaseDuration;1078 child = child.sibling;1079 }1080 completedWork.actualDuration = actualDuration;1081 completedWork.treeBaseDuration = treeBaseDuration;1082 } else {1083 let child = completedWork.child;1084 while (child !== null) {1085 const childUpdateExpirationTime = child.expirationTime;1086 const childChildExpirationTime = child.childExpirationTime;1087 if (childUpdateExpirationTime > newChildExpirationTime) {1088 newChildExpirationTime = childUpdateExpirationTime;1089 }1090 if (childChildExpirationTime > newChildExpirationTime) {1091 newChildExpirationTime = childChildExpirationTime;1092 }1093 child = child.sibling;1094 }1095 }1096 completedWork.childExpirationTime = newChildExpirationTime;1097}1098function commitRoot(root, expirationTime) {1099 runWithPriority(1100 ImmediatePriority,1101 commitRootImpl.bind(null, root, expirationTime),1102 );1103 // If there are passive effects, schedule a callback to flush them. This goes1104 // outside commitRootImpl so that it inherits the priority of the render.1105 if (rootWithPendingPassiveEffects !== null) {1106 const priorityLevel = getCurrentPriorityLevel();1107 scheduleCallback(priorityLevel, () => {1108 flushPassiveEffects();1109 return null;1110 });1111 }1112 return null;1113}1114function commitRootImpl(root, expirationTime) {1115 flushPassiveEffects();1116 flushRenderPhaseStrictModeWarningsInDEV();1117 invariant(1118 workPhase !== RenderPhase && workPhase !== CommitPhase,1119 'Should not already be working.',1120 );1121 const finishedWork = root.current.alternate;1122 invariant(finishedWork !== null, 'Should have a work-in-progress root.');1123 // commitRoot never returns a continuation; it always finishes synchronously.1124 // So we can clear these now to allow a new callback to be scheduled.1125 root.callbackNode = null;1126 root.callbackExpirationTime = NoWork;1127 startCommitTimer();1128 // Update the first and last pending times on this root. The new first1129 // pending time is whatever is left on the root fiber.1130 const updateExpirationTimeBeforeCommit = finishedWork.expirationTime;1131 const childExpirationTimeBeforeCommit = finishedWork.childExpirationTime;1132 const firstPendingTimeBeforeCommit =1133 childExpirationTimeBeforeCommit > updateExpirationTimeBeforeCommit1134 ? childExpirationTimeBeforeCommit1135 : updateExpirationTimeBeforeCommit;1136 root.firstPendingTime = firstPendingTimeBeforeCommit;1137 if (firstPendingTimeBeforeCommit < root.lastPendingTime) {1138 // This usually means we've finished all the work, but it can also happen1139 // when something gets downprioritized during render, like a hidden tree.1140 root.lastPendingTime = firstPendingTimeBeforeCommit;1141 }1142 if (root === workInProgressRoot) {1143 // We can reset these now that they are finished.1144 workInProgressRoot = null;1145 workInProgress = null;1146 renderExpirationTime = NoWork;1147 } else {1148 // This indicates that the last root we worked on is not the same one that1149 // we're committing now. This most commonly happens when a suspended root1150 // times out.1151 }1152 // Get the list of effects.1153 let firstEffect;1154 if (finishedWork.effectTag > PerformedWork) {1155 // A fiber's effect list consists only of its children, not itself. So if1156 // the root has an effect, we need to add it to the end of the list. The1157 // resulting list is the set that would belong to the root's parent, if it1158 // had one; that is, all the effects in the tree including the root.1159 if (finishedWork.lastEffect !== null) {1160 finishedWork.lastEffect.nextEffect = finishedWork;1161 firstEffect = finishedWork.firstEffect;1162 } else {1163 firstEffect = finishedWork;1164 }1165 } else {1166 // There is no effect on the root.1167 firstEffect = finishedWork.firstEffect;1168 }1169 if (firstEffect !== null) {1170 const prevWorkPhase = workPhase;1171 workPhase = CommitPhase;1172 let prevInteractions: Set<Interaction> | null = null;1173 if (enableSchedulerTracing) {1174 prevInteractions = __interactionsRef.current;1175 __interactionsRef.current = root.memoizedInteractions;1176 }1177 // Reset this to null before calling lifecycles1178 ReactCurrentOwner.current = null;1179 // The commit phase is broken into several sub-phases. We do a separate pass1180 // of the effect list for each phase: all mutation effects come before all1181 // layout effects, and so on.1182 // The first phase a "before mutation" phase. We use this phase to read the1183 // state of the host tree right before we mutate it. This is where1184 // getSnapshotBeforeUpdate is called.1185 startCommitSnapshotEffectsTimer();1186 prepareForCommit(root.containerInfo);1187 nextEffect = firstEffect;1188 do {1189 if (__DEV__) {1190 invokeGuardedCallback(null, commitBeforeMutationEffects, null);1191 if (hasCaughtError()) {1192 invariant(nextEffect !== null, 'Should be working on an effect.');1193 const error = clearCaughtError();1194 captureCommitPhaseError(nextEffect, error);1195 nextEffect = nextEffect.nextEffect;1196 }1197 } else {1198 try {1199 commitBeforeMutationEffects();1200 } catch (error) {1201 invariant(nextEffect !== null, 'Should be working on an effect.');1202 captureCommitPhaseError(nextEffect, error);1203 nextEffect = nextEffect.nextEffect;1204 }1205 }1206 } while (nextEffect !== null);1207 stopCommitSnapshotEffectsTimer();1208 if (enableProfilerTimer) {1209 // Mark the current commit time to be shared by all Profilers in this1210 // batch. This enables them to be grouped later.1211 recordCommitTime();1212 }1213 // The next phase is the mutation phase, where we mutate the host tree.1214 startCommitHostEffectsTimer();1215 nextEffect = firstEffect;1216 do {1217 if (__DEV__) {1218 invokeGuardedCallback(null, commitMutationEffects, null);1219 if (hasCaughtError()) {1220 invariant(nextEffect !== null, 'Should be working on an effect.');1221 const error = clearCaughtError();1222 captureCommitPhaseError(nextEffect, error);1223 nextEffect = nextEffect.nextEffect;1224 }1225 } else {1226 try {1227 commitMutationEffects();1228 } catch (error) {1229 invariant(nextEffect !== null, 'Should be working on an effect.');1230 captureCommitPhaseError(nextEffect, error);1231 nextEffect = nextEffect.nextEffect;1232 }1233 }1234 } while (nextEffect !== null);1235 stopCommitHostEffectsTimer();1236 resetAfterCommit(root.containerInfo);1237 // The work-in-progress tree is now the current tree. This must come after1238 // the mutation phase, so that the previous tree is still current during1239 // componentWillUnmount, but before the layout phase, so that the finished1240 // work is current during componentDidMount/Update.1241 root.current = finishedWork;1242 // The next phase is the layout phase, where we call effects that read1243 // the host tree after it's been mutated. The idiomatic use case for this is1244 // layout, but class component lifecycles also fire here for legacy reasons.1245 startCommitLifeCyclesTimer();1246 nextEffect = firstEffect;1247 do {1248 if (__DEV__) {1249 invokeGuardedCallback(1250 null,1251 commitLayoutEffects,1252 null,1253 root,1254 expirationTime,1255 );1256 if (hasCaughtError()) {1257 invariant(nextEffect !== null, 'Should be working on an effect.');1258 const error = clearCaughtError();1259 captureCommitPhaseError(nextEffect, error);1260 nextEffect = nextEffect.nextEffect;1261 }1262 } else {1263 try {1264 commitLayoutEffects(root, expirationTime);1265 } catch (error) {1266 invariant(nextEffect !== null, 'Should be working on an effect.');1267 captureCommitPhaseError(nextEffect, error);1268 nextEffect = nextEffect.nextEffect;1269 }1270 }1271 } while (nextEffect !== null);1272 stopCommitLifeCyclesTimer();1273 nextEffect = null;1274 if (enableSchedulerTracing) {1275 __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);1276 }1277 workPhase = prevWorkPhase;1278 } else {1279 // No effects.1280 root.current = finishedWork;1281 // Measure these anyway so the flamegraph explicitly shows that there were1282 // no effects.1283 // TODO: Maybe there's a better way to report this.1284 startCommitSnapshotEffectsTimer();1285 stopCommitSnapshotEffectsTimer();1286 if (enableProfilerTimer) {1287 recordCommitTime();1288 }1289 startCommitHostEffectsTimer();1290 stopCommitHostEffectsTimer();1291 startCommitLifeCyclesTimer();1292 stopCommitLifeCyclesTimer();1293 }1294 stopCommitTimer();1295 if (rootDoesHavePassiveEffects) {1296 // This commit has passive effects. Stash a reference to them. But don't1297 // schedule a callback until after flushing layout work.1298 rootDoesHavePassiveEffects = false;1299 rootWithPendingPassiveEffects = root;1300 pendingPassiveEffectsExpirationTime = expirationTime;1301 } else {1302 if (enableSchedulerTracing) {1303 // If there are no passive effects, then we can complete the pending1304 // interactions. Otherwise, we'll wait until after the passive effects1305 // are flushed.1306 finishPendingInteractions(root, expirationTime);1307 }1308 }1309 // Check if there's remaining work on this root1310 const remainingExpirationTime = root.firstPendingTime;1311 if (remainingExpirationTime !== NoWork) {1312 const currentTime = requestCurrentTime();1313 const priorityLevel = inferPriorityFromExpirationTime(1314 currentTime,1315 remainingExpirationTime,1316 );1317 scheduleCallbackForRoot(root, priorityLevel, remainingExpirationTime);1318 } else {1319 // If there's no remaining work, we can clear the set of already failed1320 // error boundaries.1321 legacyErrorBoundariesThatAlreadyFailed = null;1322 }1323 onCommitRoot(finishedWork.stateNode);1324 if (remainingExpirationTime === Sync) {1325 // Count the number of times the root synchronously re-renders without1326 // finishing. If there are too many, it indicates an infinite update loop.1327 if (root === rootWithNestedUpdates) {1328 nestedUpdateCount++;1329 } else {1330 nestedUpdateCount = 0;1331 rootWithNestedUpdates = root;1332 }1333 } else {1334 nestedUpdateCount = 0;1335 }1336 if (hasUncaughtError) {1337 hasUncaughtError = false;1338 const error = firstUncaughtError;1339 firstUncaughtError = null;1340 throw error;1341 }1342 if (workPhase === LegacyUnbatchedPhase) {1343 // This is a legacy edge case. We just committed the initial mount of1344 // a ReactDOM.render-ed root inside of batchedUpdates. The commit fired1345 // synchronously, but layout updates should be deferred until the end1346 // of the batch.1347 return null;1348 }1349 // If layout work was scheduled, flush it now.1350 flushImmediateQueue();1351 return null;1352}1353function commitBeforeMutationEffects() {1354 while (nextEffect !== null) {1355 if ((nextEffect.effectTag & Snapshot) !== NoEffect) {1356 setCurrentDebugFiberInDEV(nextEffect);1357 recordEffect();1358 const current = nextEffect.alternate;1359 commitBeforeMutationEffectOnFiber(current, nextEffect);1360 resetCurrentDebugFiberInDEV();1361 }1362 nextEffect = nextEffect.nextEffect;1363 }1364}1365function commitMutationEffects() {1366 // TODO: Should probably move the bulk of this function to commitWork.1367 while (nextEffect !== null) {1368 setCurrentDebugFiberInDEV(nextEffect);1369 const effectTag = nextEffect.effectTag;1370 if (effectTag & ContentReset) {1371 commitResetTextContent(nextEffect);1372 }1373 if (effectTag & Ref) {1374 const current = nextEffect.alternate;1375 if (current !== null) {1376 commitDetachRef(current);1377 }1378 }1379 // The following switch statement is only concerned about placement,1380 // updates, and deletions. To avoid needing to add a case for every possible1381 // bitmap value, we remove the secondary effects from the effect tag and1382 // switch on that value.1383 let primaryEffectTag = effectTag & (Placement | Update | Deletion);1384 switch (primaryEffectTag) {1385 case Placement: {1386 commitPlacement(nextEffect);1387 // Clear the "placement" from effect tag so that we know that this is1388 // inserted, before any life-cycles like componentDidMount gets called.1389 // TODO: findDOMNode doesn't rely on this any more but isMounted does1390 // and isMounted is deprecated anyway so we should be able to kill this.1391 nextEffect.effectTag &= ~Placement;1392 break;1393 }1394 case PlacementAndUpdate: {1395 // Placement1396 commitPlacement(nextEffect);1397 // Clear the "placement" from effect tag so that we know that this is1398 // inserted, before any life-cycles like componentDidMount gets called.1399 nextEffect.effectTag &= ~Placement;1400 // Update1401 const current = nextEffect.alternate;1402 commitWork(current, nextEffect);1403 break;1404 }1405 case Update: {1406 const current = nextEffect.alternate;1407 commitWork(current, nextEffect);1408 break;1409 }1410 case Deletion: {1411 commitDeletion(nextEffect);1412 break;1413 }1414 }1415 // TODO: Only record a mutation effect if primaryEffectTag is non-zero.1416 recordEffect();1417 resetCurrentDebugFiberInDEV();1418 nextEffect = nextEffect.nextEffect;1419 }1420}1421function commitLayoutEffects(1422 root: FiberRoot,1423 committedExpirationTime: ExpirationTime,1424) {1425 // TODO: Should probably move the bulk of this function to commitWork.1426 while (nextEffect !== null) {1427 setCurrentDebugFiberInDEV(nextEffect);1428 const effectTag = nextEffect.effectTag;1429 if (effectTag & (Update | Callback)) {1430 recordEffect();1431 const current = nextEffect.alternate;1432 commitLayoutEffectOnFiber(1433 root,1434 current,1435 nextEffect,1436 committedExpirationTime,1437 );1438 }1439 if (effectTag & Ref) {1440 recordEffect();1441 commitAttachRef(nextEffect);1442 }1443 if (effectTag & Passive) {1444 rootDoesHavePassiveEffects = true;1445 }1446 resetCurrentDebugFiberInDEV();1447 nextEffect = nextEffect.nextEffect;1448 }1449}1450export function flushPassiveEffects() {1451 if (rootWithPendingPassiveEffects === null) {1452 return false;1453 }1454 const root = rootWithPendingPassiveEffects;1455 const expirationTime = pendingPassiveEffectsExpirationTime;1456 rootWithPendingPassiveEffects = null;1457 pendingPassiveEffectsExpirationTime = NoWork;1458 let prevInteractions: Set<Interaction> | null = null;1459 if (enableSchedulerTracing) {1460 prevInteractions = __interactionsRef.current;1461 __interactionsRef.current = root.memoizedInteractions;1462 }1463 invariant(1464 workPhase !== RenderPhase && workPhase !== CommitPhase,1465 'Cannot flush passive effects while already rendering.',1466 );1467 const prevWorkPhase = workPhase;1468 workPhase = CommitPhase;1469 // Note: This currently assumes there are no passive effects on the root1470 // fiber, because the root is not part of its own effect list. This could1471 // change in the future.1472 let effect = root.current.firstEffect;1473 while (effect !== null) {1474 if (__DEV__) {1475 setCurrentDebugFiberInDEV(effect);1476 invokeGuardedCallback(null, commitPassiveHookEffects, null, effect);1477 if (hasCaughtError()) {1478 invariant(effect !== null, 'Should be working on an effect.');1479 const error = clearCaughtError();1480 captureCommitPhaseError(effect, error);1481 }1482 resetCurrentDebugFiberInDEV();1483 } else {1484 try {1485 commitPassiveHookEffects(effect);1486 } catch (error) {1487 invariant(effect !== null, 'Should be working on an effect.');1488 captureCommitPhaseError(effect, error);1489 }1490 }1491 effect = effect.nextEffect;1492 }1493 if (enableSchedulerTracing) {1494 __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);1495 finishPendingInteractions(root, expirationTime);1496 }1497 workPhase = prevWorkPhase;1498 flushImmediateQueue();1499 // If additional passive effects were scheduled, increment a counter. If this1500 // exceeds the limit, we'll fire a warning.1501 nestedPassiveUpdateCount =1502 rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;1503 return true;1504}1505export function isAlreadyFailedLegacyErrorBoundary(instance: mixed): boolean {1506 return (1507 legacyErrorBoundariesThatAlreadyFailed !== null &&1508 legacyErrorBoundariesThatAlreadyFailed.has(instance)1509 );1510}1511export function markLegacyErrorBoundaryAsFailed(instance: mixed) {1512 if (legacyErrorBoundariesThatAlreadyFailed === null) {1513 legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);1514 } else {1515 legacyErrorBoundariesThatAlreadyFailed.add(instance);1516 }1517}1518function prepareToThrowUncaughtError(error: mixed) {1519 if (!hasUncaughtError) {1520 hasUncaughtError = true;1521 firstUncaughtError = error;1522 }1523}1524export const onUncaughtError = prepareToThrowUncaughtError;1525function captureCommitPhaseErrorOnRoot(1526 rootFiber: Fiber,1527 sourceFiber: Fiber,1528 error: mixed,1529) {1530 const errorInfo = createCapturedValue(error, sourceFiber);1531 const update = createRootErrorUpdate(rootFiber, errorInfo, Sync);1532 enqueueUpdate(rootFiber, update);1533 const root = markUpdateTimeFromFiberToRoot(rootFiber, Sync);1534 if (root !== null) {1535 scheduleCallbackForRoot(root, ImmediatePriority, Sync);1536 }1537}1538export function captureCommitPhaseError(sourceFiber: Fiber, error: mixed) {1539 if (sourceFiber.tag === HostRoot) {1540 // Error was thrown at the root. There is no parent, so the root1541 // itself should capture it.1542 captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);1543 return;1544 }1545 let fiber = sourceFiber.return;1546 while (fiber !== null) {1547 if (fiber.tag === HostRoot) {1548 captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);1549 return;1550 } else if (fiber.tag === ClassComponent) {1551 const ctor = fiber.type;1552 const instance = fiber.stateNode;1553 if (1554 typeof ctor.getDerivedStateFromError === 'function' ||1555 (typeof instance.componentDidCatch === 'function' &&1556 !isAlreadyFailedLegacyErrorBoundary(instance))1557 ) {1558 const errorInfo = createCapturedValue(error, sourceFiber);1559 const update = createClassErrorUpdate(1560 fiber,1561 errorInfo,1562 // TODO: This is always sync1563 Sync,1564 );1565 enqueueUpdate(fiber, update);1566 const root = markUpdateTimeFromFiberToRoot(fiber, Sync);1567 if (root !== null) {1568 scheduleCallbackForRoot(root, ImmediatePriority, Sync);1569 }1570 return;1571 }1572 }1573 fiber = fiber.return;1574 }1575}1576export function pingSuspendedRoot(1577 root: FiberRoot,1578 thenable: Thenable,1579 suspendedTime: ExpirationTime,1580) {1581 const pingCache = root.pingCache;1582 if (pingCache !== null) {1583 // The thenable resolved, so we no longer need to memoize, because it will1584 // never be thrown again.1585 pingCache.delete(thenable);1586 }1587 if (workInProgressRoot === root && renderExpirationTime === suspendedTime) {1588 // Received a ping at the same priority level at which we're currently1589 // rendering. Restart from the root. Don't need to schedule a ping because1590 // we're already working on this tree.1591 prepareFreshStack(root, renderExpirationTime);1592 return;1593 }1594 const lastPendingTime = root.lastPendingTime;1595 if (lastPendingTime < suspendedTime) {1596 // The root is no longer suspended at this time.1597 return;1598 }1599 const pingTime = root.pingTime;1600 if (pingTime !== NoWork && pingTime < suspendedTime) {1601 // There's already a lower priority ping scheduled.1602 return;1603 }1604 // Mark the time at which this ping was scheduled.1605 root.pingTime = suspendedTime;1606 const currentTime = requestCurrentTime();1607 const priorityLevel = inferPriorityFromExpirationTime(1608 currentTime,1609 suspendedTime,1610 );1611 scheduleCallbackForRoot(root, priorityLevel, suspendedTime);1612}1613export function retryTimedOutBoundary(boundaryFiber: Fiber) {1614 // The boundary fiber (a Suspense component) previously timed out and was1615 // rendered in its fallback state. One of the promises that suspended it has1616 // resolved, which means at least part of the tree was likely unblocked. Try1617 // rendering again, at a new expiration time.1618 const currentTime = requestCurrentTime();1619 const retryTime = computeExpirationForFiber(currentTime, boundaryFiber);1620 // TODO: Special case idle priority?1621 const priorityLevel = inferPriorityFromExpirationTime(currentTime, retryTime);1622 const root = markUpdateTimeFromFiberToRoot(boundaryFiber, retryTime);1623 if (root !== null) {1624 scheduleCallbackForRoot(root, priorityLevel, retryTime);1625 }1626}1627export function resolveRetryThenable(boundaryFiber: Fiber, thenable: Thenable) {1628 let retryCache: WeakSet<Thenable> | Set<Thenable> | null;1629 if (enableSuspenseServerRenderer) {1630 switch (boundaryFiber.tag) {1631 case SuspenseComponent:1632 retryCache = boundaryFiber.stateNode;1633 break;1634 case DehydratedSuspenseComponent:1635 retryCache = boundaryFiber.memoizedState;1636 break;1637 default:1638 invariant(1639 false,1640 'Pinged unknown suspense boundary type. ' +1641 'This is probably a bug in React.',1642 );1643 }1644 } else {1645 retryCache = boundaryFiber.stateNode;1646 }1647 if (retryCache !== null) {1648 // The thenable resolved, so we no longer need to memoize, because it will1649 // never be thrown again.1650 retryCache.delete(thenable);1651 }1652 retryTimedOutBoundary(boundaryFiber);1653}1654// Computes the next Just Noticeable Difference (JND) boundary.1655// The theory is that a person can't tell the difference between small differences in time.1656// Therefore, if we wait a bit longer than necessary that won't translate to a noticeable1657// difference in the experience. However, waiting for longer might mean that we can avoid1658// showing an intermediate loading state. The longer we have already waited, the harder it1659// is to tell small differences in time. Therefore, the longer we've already waited,1660// the longer we can wait additionally. At some point we have to give up though.1661// We pick a train model where the next boundary commits at a consistent schedule.1662// These particular numbers are vague estimates. We expect to adjust them based on research.1663function jnd(timeElapsed: number) {1664 return timeElapsed < 1201665 ? 1201666 : timeElapsed < 4801667 ? 4801668 : timeElapsed < 10801669 ? 10801670 : timeElapsed < 19201671 ? 19201672 : timeElapsed < 30001673 ? 30001674 : timeElapsed < 43201675 ? 43201676 : ceil(timeElapsed / 1960) * 1960;1677}1678function computeMsUntilTimeout(1679 mostRecentEventTime: ExpirationTime,1680 committedExpirationTime: ExpirationTime,1681) {1682 if (disableYielding) {1683 // Timeout immediately when yielding is disabled.1684 return 0;1685 }1686 const eventTimeMs: number = inferTimeFromExpirationTime(mostRecentEventTime);1687 const currentTimeMs: number = now();1688 const timeElapsed = currentTimeMs - eventTimeMs;1689 let msUntilTimeout = jnd(timeElapsed) - timeElapsed;1690 // Compute the time until this render pass would expire.1691 const timeUntilExpirationMs =1692 expirationTimeToMs(committedExpirationTime) - currentTimeMs;1693 // Clamp the timeout to the expiration time.1694 // TODO: Once the event time is exact instead of inferred from expiration time1695 // we don't need this.1696 if (timeUntilExpirationMs < msUntilTimeout) {1697 msUntilTimeout = timeUntilExpirationMs;1698 }1699 // This is the value that is passed to `setTimeout`.1700 return msUntilTimeout;...
Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch();4 const context = await browser.newContext();5 const page = await context.newPage();6 const cookies = await page.context().cookies();7 console.log(cookies)8 await browser.close();9})();10 {11 }
Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch();4 const page = await browser.newPage();5 await page.screenshot({ path: 'example.png' });6 console.log(await page.evaluate(() => window.playwright.inferTimeFromExpirationTime(0)));7 await browser.close();8})();9const { chromium } = require('playwright');10(async () => {11 const browser = await chromium.launch();12 const page = await browser.newPage();13 await page.screenshot({ path: 'example.png' });14 console.log(await page.evaluate(() => window.playwright.inferTimeFromExpirationTime(1)));15 await browser.close();16})();17const { chromium } = require('playwright');18(async () => {19 const browser = await chromium.launch();20 const page = await browser.newPage();21 await page.screenshot({ path: 'example.png' });22 console.log(await page.evaluate(() => window.playwright.inferTimeFromExpirationTime(2)));23 await browser.close();24})();25const { chromium } = require('playwright');26(async () => {27 const browser = await chromium.launch();28 const page = await browser.newPage();29 await page.screenshot({ path: 'example.png' });30 console.log(await page.evaluate(() => window.playwright.inferTimeFromExpirationTime(3)));31 await browser.close();32})();33const { chromium } = require('playwright');34(async () => {35 const browser = await chromium.launch();36 const page = await browser.newPage();37 await page.screenshot({ path: 'example.png' });
Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch();4 const context = await browser.newContext();5 const page = await context.newPage();6 const internal = page._delegate;7 const expiration = internal.inferTimeFromExpirationTime('1d');8 console.log(expiration);9 await browser.close();10})();11const { chromium } = require('playwright');12(async () => {13 const browser = await chromium.launch();14 const context = await browser.newContext();15 const page = await context.newPage();16 const internal = page._delegate;17 const expiration = internal.inferTimeFromExpirationTime('1d');18 console.log(expiration);19 await browser.close();20})();21 at async Object.exports.createTarget (/home/ankit/playwright-test/node_modules/playwright-core/lib/cjs/pw-run.js:1:1271)22 at async BrowserContext._createPageInNewContext (/home/ankit/playwright-test/node_modules/playwright-core/lib/cjs/pw-run.js:1:11113)23 at async BrowserContext.newPage (/home/ankit/playwright-test/node_modules/playwright-core/lib/cjs/pw-run.js:1:10521)24 at async Object.<anonymous> (/home/ankit/playwright-test/test.js:7:15)
Using AI Code Generation
1const { inferTimeFromExpirationTime } = require('playwright/lib/internal/utils');2const { inferTimeFromExpirationTime } = require('playwright/lib/internal/utils');3const time = inferTimeFromExpirationTime(120);4console.log(time);5const { inferTimeFromExpirationTime } = require('playwright/lib/internal/utils');6const time = inferTimeFromExpirationTime(120);7console.log(time);8const { inferTimeFromExpirationTime } = require('playwright/lib/internal/utils');9const time = inferTimeFromExpirationTime(120);10console.log(time);11const { inferTimeFromExpirationTime } = require('playwright/lib/internal/utils');12const time = inferTimeFromExpirationTime(120);13console.log(time);14const { inferTimeFromExpirationTime } = require('playwright/lib/internal/utils');15const time = inferTimeFromExpirationTime(120);16console.log(time);17const { inferTimeFromExpirationTime } = require('playwright/lib/internal/utils');18const time = inferTimeFromExpirationTime(120);19console.log(time);20const { inferTimeFromExpirationTime } = require('playwright/lib/internal/utils');21const time = inferTimeFromExpirationTime(120);22console.log(time);23const { inferTimeFromExpirationTime } = require('play
Using AI Code Generation
1const { Playwright } = require('playwright');2const { inferTimeFromExpirationTime } = Playwright.Internal;3const time = inferTimeFromExpirationTime(3600);4console.log(time);5const { Playwright } = require('playwright');6const { inferTimeFromExpirationTime } = Playwright.Internal;7const time = inferTimeFromExpirationTime(3600);8console.log(time);9const { Playwright } = require('playwright');10const { inferTimeFromExpirationTime } = Playwright.Internal;11const time = inferTimeFromExpirationTime(3600);12console.log(time);13const { Playwright } = require('playwright');14const { inferTimeFromExpirationTime } = Playwright.Internal;15const time = inferTimeFromExpirationTime(3600);16console.log(time);17const { Playwright } = require('playwright');18const { inferTimeFromExpirationTime } = Playwright.Internal;19const time = inferTimeFromExpirationTime(3600);20console.log(time);21const { Playwright } = require('playwright');22const { inferTimeFromExpirationTime } = Playwright.Internal;23const time = inferTimeFromExpirationTime(3600);24console.log(time);25const { Playwright }
Using AI Code Generation
1const { devices } = require('playwright-core');2const { inferTimeFromExpirationTime } = require('playwright-core/lib/server/frames');3const { Page } = require('playwright-core/lib/server/page');4const { Frame } = require('playwright-core/lib/server/frame');5const { JSHandle } = require('playwright-core/lib/server/jsHandle');6const iPhone = devices['iPhone 6'];7const page = new Page();8const frame = new Frame(page, 'mainFrameId', 'about:blank');9const jsHandle = new JSHandle(frame, 'object', {});10const expirationTime = 1000;11const time = inferTimeFromExpirationTime(expirationTime, jsHandle);12console.log(time);
Using AI Code Generation
1const { inferTimeFromExpirationTime } = require('playwright/lib/utils/utils');2const time = inferTimeFromExpirationTime(3600);3console.log(time);4const { inferTimeFromExpirationTime } = require('playwright/lib/utils/utils');5const time = inferTimeFromExpirationTime(7200);6console.log(time);7const { inferTimeFromExpirationTime } = require('playwright/lib/utils/utils');8const time = inferTimeFromExpirationTime(10800);9console.log(time);10const { inferTimeFromExpirationTime } = require('playwright/lib/utils/utils');11const time = inferTimeFromExpirationTime(86400);12console.log(time);13const { inferTimeFromExpirationTime } = require('playwright/lib/utils/utils');14const time = inferTimeFromExpirationTime(172800);15console.log(time);16const { inferTimeFromExpirationTime } = require('playwright/lib/utils/utils');17const time = inferTimeFromExpirationTime(259200);18console.log(time);19const { inferTimeFromExpirationTime } = require('playwright/lib/utils/utils');20const time = inferTimeFromExpirationTime(604800);21console.log(time);22const { inferTimeFromExpirationTime } = require('playwright/lib/utils/utils');23const time = inferTimeFromExpirationTime(1209600);24console.log(time);
Using AI Code Generation
1const {BrowserContext, Page} = require('playwright');2const {createPageInContext} = require('playwright/lib/server/browserContext');3const {createPage} = require('playwright/lib/server/page');4const {createFrame} = require('playwright/lib/server/frames');5const {createExecutionContext} = require('playwright/lib/server/executionContext');6const {createJSHandle} = require('playwright/lib/server/javascript');7const {createCDPSession} = require('playwright/lib/server/cdpsession');8const {createNetworkManager} = require('playwright/lib/server/network');9const {createTimeoutSettings} = require('playwright/lib/server/timeouts');10const {createCoverage} = require('playwright/lib/server/coverage');11const {createTracing} = require('playwright/lib/server/tracing');12const {createDownloadManager} = require('playwright/lib/server/download');13const {createFileChooser} = require('playwright/lib/server/fileChooser');14const {createAccessibility} = require('playwright/lib/server/accessibility');15const {createDialog} = require('playwright/lib/server/dialog');16const {createVideo} = require('playwright/lib/server/video');17const {createWebsocketTransport} = require('playwright/lib/server/transport');18const path = require('path');19const fs = require('fs');20const os = require('os');21const {createInProcessTransport} = require('playwright/lib/server/transport');22const {createBrowserServer} = require('playwright/lib/server/browserServer');23const {createBrowserType} = require('playwright/lib/server/browserType');24const {createBrowser} = require('playwright/lib/server/browser');25const {createBrowserContext} = require('playwright/lib/server/browserContext');26const {createPage} = require('playwright/lib/server/page');27const {createFrame} = require('playwright/lib/server/frames');28const {createExecutionContext} = require('playwright/lib/server/executionContext');29const {createJSHandle} = require('playwright/lib/server/javascript');30const {createCDPSession} = require('playwright/lib/server/cdpsession');31const {createNetworkManager} = require('playwright/lib/server/network');32const {createTimeoutSettings} = require('playwright/lib/server/timeouts');33const {createCoverage} = require('playwright/lib/server/coverage');34const {createTracing} = require('playwright
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!!