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('playUsing 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('playwrightLambdaTest’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!!
