Best JavaScript code snippet using playwright-internal
ReactFiberWorkLoop.js
Source:ReactFiberWorkLoop.js  
...351  if (root === null) {352    warnAboutUpdateOnUnmountedFiberInDEV(fiber);353    return;354  }355  checkForInterruption(fiber, expirationTime);356  recordScheduleUpdate();357  // TODO: computeExpirationForFiber also reads the priority. Pass the358  // priority as an argument to that function and this one.359  const priorityLevel = getCurrentPriorityLevel();360  if (expirationTime === Sync) {361    if (362      // Check if we're inside unbatchedUpdates363      (executionContext & LegacyUnbatchedContext) !== NoContext &&364      // Check if we're not already rendering365      (executionContext & (RenderContext | CommitContext)) === NoContext366    ) {367      // Register pending interactions on the root to avoid losing traced interaction data.368      schedulePendingInteractions(root, expirationTime);369      // This is a legacy edge case. The initial mount of a ReactDOM.render-ed370      // root inside of batchedUpdates should be synchronous, but layout updates371      // should be deferred until the end of the batch.372      debugger;373      performSyncWorkOnRoot(root);374    } else {375      ensureRootIsScheduled(root);376      schedulePendingInteractions(root, expirationTime);377      if (executionContext === NoContext) {378        // Flush the synchronous work now, unless we're already working or inside379        // a batch. This is intentionally inside scheduleUpdateOnFiber instead of380        // scheduleCallbackForFiber to preserve the ability to schedule a callback381        // without immediately flushing it. We only do this for user-initiated382        // updates, to preserve historical behavior of legacy mode.383        flushSyncCallbackQueue();384      }385    }386  } else {387    ensureRootIsScheduled(root);388    schedulePendingInteractions(root, expirationTime);389  }390  if (391    (executionContext & DiscreteEventContext) !== NoContext &&392    // Only updates at user-blocking priority or greater are considered393    // discrete, even inside a discrete event.394    (priorityLevel === UserBlockingPriority ||395      priorityLevel === ImmediatePriority)396  ) {397    // This is the result of a discrete event. Track the lowest priority398    // discrete update per root so we can flush them early, if needed.399    if (rootsWithPendingDiscreteUpdates === null) {400      rootsWithPendingDiscreteUpdates = new Map([[root, expirationTime]]);401    } else {402      const lastDiscreteTime = rootsWithPendingDiscreteUpdates.get(root);403      if (lastDiscreteTime === undefined || lastDiscreteTime > expirationTime) {404        rootsWithPendingDiscreteUpdates.set(root, expirationTime);405      }406    }407  }408}409export const scheduleWork = scheduleUpdateOnFiber;410// This is split into a separate function so we can mark a fiber with pending411// work without treating it as a typical update that originates from an event;412// e.g. retrying a Suspense boundary isn't an update, but it does schedule work413// on a fiber.414function markUpdateTimeFromFiberToRoot(fiber, expirationTime) {415  // Update the source fiber's expiration time416  if (fiber.expirationTime < expirationTime) {417    fiber.expirationTime = expirationTime;418  }419  let alternate = fiber.alternate;420  if (alternate !== null && alternate.expirationTime < expirationTime) {421    alternate.expirationTime = expirationTime;422  }423  // Walk the parent path to the root and update the child expiration time.424  let node = fiber.return;425  let root = null;426  if (node === null && fiber.tag === HostRoot) {427    root = fiber.stateNode;428  } else {429    while (node !== null) {430      alternate = node.alternate;431      if (node.childExpirationTime < expirationTime) {432        node.childExpirationTime = expirationTime;433        if (434          alternate !== null &&435          alternate.childExpirationTime < expirationTime436        ) {437          alternate.childExpirationTime = expirationTime;438        }439      } else if (440        alternate !== null &&441        alternate.childExpirationTime < expirationTime442      ) {443        alternate.childExpirationTime = expirationTime;444      }445      if (node.return === null && node.tag === HostRoot) {446        root = node.stateNode;447        break;448      }449      node = node.return;450    }451  }452  if (root !== null) {453    if (workInProgressRoot === root) {454      // Received an update to a tree that's in the middle of rendering. Mark455      // that's unprocessed work on this root.456      markUnprocessedUpdateTime(expirationTime);457      if (workInProgressRootExitStatus === RootSuspendedWithDelay) {458        // The root already suspended with a delay, which means this render459        // definitely won't finish. Since we have a new update, let's mark it as460        // suspended now, right before marking the incoming update. This has the461        // effect of interrupting the current render and switching to the update.462        // TODO: This happens to work when receiving an update during the render463        // phase, because of the trick inside computeExpirationForFiber to464        // subtract 1 from `renderExpirationTime` to move it into a465        // separate bucket. But we should probably model it with an exception,466        // using the same mechanism we use to force hydration of a subtree.467        // TODO: This does not account for low pri updates that were already468        // scheduled before the root started rendering. Need to track the next469        // pending expiration time (perhaps by backtracking the return path) and470        // then trigger a restart in the `renderDidSuspendDelayIfPossible` path.471        markRootSuspendedAtTime(root, renderExpirationTime);472      }473    }474    // Mark that the root has a pending update.475    markRootUpdatedAtTime(root, expirationTime);476  }477  return root;478}479function getNextRootExpirationTimeToWorkOn(root: FiberRoot): ExpirationTime {480  // Determines the next expiration time that the root should render, taking481  // into account levels that may be suspended, or levels that may have482  // received a ping.483  const lastExpiredTime = root.lastExpiredTime;484  if (lastExpiredTime !== NoWork) {485    return lastExpiredTime;486  }487  // "Pending" refers to any update that hasn't committed yet, including if it488  // suspended. The "suspended" range is therefore a subset.489  const firstPendingTime = root.firstPendingTime;490  if (!isRootSuspendedAtTime(root, firstPendingTime)) {491    // The highest priority pending time is not suspended. Let's work on that.492    return firstPendingTime;493  }494  // If the first pending time is suspended, check if there's a lower priority495  // pending level that we know about. Or check if we received a ping. Work496  // on whichever is higher priority.497  const lastPingedTime = root.lastPingedTime;498  const nextKnownPendingLevel = root.nextKnownPendingLevel;499  const nextLevel =500    lastPingedTime > nextKnownPendingLevel501      ? lastPingedTime502      : nextKnownPendingLevel;503  if (504    enableTrainModelFix &&505    nextLevel <= Idle &&506    firstPendingTime !== nextLevel507  ) {508    // Don't work on Idle/Never priority unless everything else is committed.509    return NoWork;510  }511  return nextLevel;512}513// Use this function to schedule a task for a root. There's only one task per514// root; if a task was already scheduled, we'll check to make sure the515// expiration time of the existing task is the same as the expiration time of516// the next level that the root has work on. This function is called on every517// update, and right before exiting a task.518function ensureRootIsScheduled(root: FiberRoot) {519  const lastExpiredTime = root.lastExpiredTime;520  if (lastExpiredTime !== NoWork) {521    // Special case: Expired work should flush synchronously.522    root.callbackExpirationTime = Sync;523    root.callbackPriority = ImmediatePriority;524    root.callbackNode = scheduleSyncCallback(525      performSyncWorkOnRoot.bind(null, root),526    );527    return;528  }529  const expirationTime = getNextRootExpirationTimeToWorkOn(root);530  const existingCallbackNode = root.callbackNode;531  if (expirationTime === NoWork) {532    // There's nothing to work on.533    if (existingCallbackNode !== null) {534      root.callbackNode = null;535      root.callbackExpirationTime = NoWork;536      root.callbackPriority = NoPriority;537    }538    return;539  }540  // TODO: If this is an update, we already read the current time. Pass the541  // time as an argument.542  const currentTime = requestCurrentTimeForUpdate();543  const priorityLevel = inferPriorityFromExpirationTime(544    currentTime,545    expirationTime,546  );547  // If there's an existing render task, confirm it has the correct priority and548  // expiration time. Otherwise, we'll cancel it and schedule a new one.549  if (existingCallbackNode !== null) {550    const existingCallbackPriority = root.callbackPriority;551    const existingCallbackExpirationTime = root.callbackExpirationTime;552    if (553      // Callback must have the exact same expiration time.554      existingCallbackExpirationTime === expirationTime &&555      // Callback must have greater or equal priority.556      existingCallbackPriority >= priorityLevel557    ) {558      // Existing callback is sufficient.559      return;560    }561    // Need to schedule a new task.562    // TODO: Instead of scheduling a new task, we should be able to change the563    // priority of the existing one.564    cancelCallback(existingCallbackNode);565  }566  root.callbackExpirationTime = expirationTime;567  root.callbackPriority = priorityLevel;568  let callbackNode;569  if (expirationTime === Sync) {570    // Sync React callbacks are scheduled on a special internal queue571    debugger;572    callbackNode = scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));573  } else if (disableSchedulerTimeoutBasedOnReactExpirationTime) {574    callbackNode = scheduleCallback(575      priorityLevel,576      performConcurrentWorkOnRoot.bind(null, root),577    );578  } else {579    callbackNode = scheduleCallback(580      priorityLevel,581      performConcurrentWorkOnRoot.bind(null, root),582      // Compute a task timeout based on the expiration time. This also affects583      // ordering because tasks are processed in timeout order.584      {timeout: expirationTimeToMs(expirationTime) - now()},585    );586  }587  root.callbackNode = callbackNode;588}589// This is the entry point for every concurrent task, i.e. anything that590// goes through Scheduler.591function performConcurrentWorkOnRoot(root, didTimeout) {592  // Since we know we're in a React event, we can clear the current593  // event time. The next update will compute a new event time.594  currentEventTime = NoWork;595  if (didTimeout) {596    // The render task took too long to complete. Mark the current time as597    // expired to synchronously render all expired work in a single batch.598    const currentTime = requestCurrentTimeForUpdate();599    markRootExpiredAtTime(root, currentTime);600    // This will schedule a synchronous callback.601    ensureRootIsScheduled(root);602    return null;603  }604  // Determine the next expiration time to work on, using the fields stored605  // on the root.606  const expirationTime = getNextRootExpirationTimeToWorkOn(root);607  if (expirationTime !== NoWork) {608    const originalCallbackNode = root.callbackNode;609    invariant(610      (executionContext & (RenderContext | CommitContext)) === NoContext,611      'Should not already be working.',612    );613    flushPassiveEffects();614    // If the root or expiration time have changed, throw out the existing stack615    // and prepare a fresh one. Otherwise we'll continue where we left off.616    if (617      root !== workInProgressRoot ||618      expirationTime !== renderExpirationTime619    ) {620      prepareFreshStack(root, expirationTime);621      startWorkOnPendingInteractions(root, expirationTime);622    }623    // If we have a work-in-progress fiber, it means there's still work to do624    // in this root.625    if (workInProgress !== null) {626      const prevExecutionContext = executionContext;627      executionContext |= RenderContext;628      const prevDispatcher = pushDispatcher(root);629      const prevInteractions = pushInteractions(root);630      startWorkLoopTimer(workInProgress);631      do {632        try {633          workLoopConcurrent();634          break;635        } catch (thrownValue) {636          handleError(root, thrownValue);637        }638      } while (true);639      resetContextDependencies();640      executionContext = prevExecutionContext;641      popDispatcher(prevDispatcher);642      if (enableSchedulerTracing) {643        popInteractions(((prevInteractions: any): Set<Interaction>));644      }645      if (workInProgressRootExitStatus === RootFatalErrored) {646        const fatalError = workInProgressRootFatalError;647        stopInterruptedWorkLoopTimer();648        prepareFreshStack(root, expirationTime);649        markRootSuspendedAtTime(root, expirationTime);650        ensureRootIsScheduled(root);651        throw fatalError;652      }653      if (workInProgress !== null) {654        // There's still work left over. Exit without committing.655        stopInterruptedWorkLoopTimer();656      } else {657        // We now have a consistent tree. The next step is either to commit it,658        // or, if something suspended, wait to commit it after a timeout.659        stopFinishedWorkLoopTimer();660        const finishedWork: Fiber = ((root.finishedWork =661          root.current.alternate): any);662        root.finishedExpirationTime = expirationTime;663        finishConcurrentRender(664          root,665          finishedWork,666          workInProgressRootExitStatus,667          expirationTime,668        );669      }670      ensureRootIsScheduled(root);671      if (root.callbackNode === originalCallbackNode) {672        // The task node scheduled for this root is the same one that's673        // currently executed. Need to return a continuation.674        return performConcurrentWorkOnRoot.bind(null, root);675      }676    }677  }678  return null;679}680function finishConcurrentRender(681  root,682  finishedWork,683  exitStatus,684  expirationTime,685) {686  // Set this to null to indicate there's no in-progress render.687  workInProgressRoot = null;688  switch (exitStatus) {689    case RootIncomplete:690    case RootFatalErrored: {691      invariant(false, 'Root did not complete. This is a bug in React.');692    }693    // Flow knows about invariant, so it complains if I add a break694    // statement, but eslint doesn't know about invariant, so it complains695    // if I do. eslint-disable-next-line no-fallthrough696    case RootErrored: {697      // If this was an async render, the error may have happened due to698      // a mutation in a concurrent event. Try rendering one more time,699      // synchronously, to see if the error goes away. If there are700      // lower priority updates, let's include those, too, in case they701      // fix the inconsistency. Render at Idle to include all updates.702      // If it was Idle or Never or some not-yet-invented time, render703      // at that time.704      markRootExpiredAtTime(705        root,706        expirationTime > Idle ? Idle : expirationTime,707      );708      // We assume that this second render pass will be synchronous709      // and therefore not hit this path again.710      break;711    }712    case RootSuspended: {713      markRootSuspendedAtTime(root, expirationTime);714      const lastSuspendedTime = root.lastSuspendedTime;715      if (expirationTime === lastSuspendedTime) {716        root.nextKnownPendingLevel = getRemainingExpirationTime(finishedWork);717      }718      // We have an acceptable loading state. We need to figure out if we719      // should immediately commit it or wait a bit.720      // If we have processed new updates during this render, we may now721      // have a new loading state ready. We want to ensure that we commit722      // that as soon as possible.723      const hasNotProcessedNewUpdates =724        workInProgressRootLatestProcessedExpirationTime === Sync;725      if (726        hasNotProcessedNewUpdates &&727        // do not delay if we're inside an act() scope728        !(729          true &&730          flushSuspenseFallbacksInTests &&731          IsThisRendererActing.current732        )733      ) {734        // If we have not processed any new updates during this pass, then735        // this is either a retry of an existing fallback state or a736        // hidden tree. Hidden trees shouldn't be batched with other work737        // and after that's fixed it can only be a retry. We're going to738        // throttle committing retries so that we don't show too many739        // loading states too quickly.740        let msUntilTimeout =741          globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now();742        // Don't bother with a very short suspense time.743        if (msUntilTimeout > 10) {744          if (workInProgressRootHasPendingPing) {745            const lastPingedTime = root.lastPingedTime;746            if (lastPingedTime === NoWork || lastPingedTime >= expirationTime) {747              // This render was pinged but we didn't get to restart748              // earlier so try restarting now instead.749              root.lastPingedTime = expirationTime;750              prepareFreshStack(root, expirationTime);751              break;752            }753          }754          const nextTime = getNextRootExpirationTimeToWorkOn(root);755          if (nextTime !== NoWork && nextTime !== expirationTime) {756            // There's additional work on this root.757            break;758          }759          if (760            lastSuspendedTime !== NoWork &&761            lastSuspendedTime !== expirationTime762          ) {763            // We should prefer to render the fallback of at the last764            // suspended level. Ping the last suspended level to try765            // rendering it again.766            root.lastPingedTime = lastSuspendedTime;767            break;768          }769          // The render is suspended, it hasn't timed out, and there's no770          // lower priority work to do. Instead of committing the fallback771          // immediately, wait for more data to arrive.772          root.timeoutHandle = scheduleTimeout(773            commitRoot.bind(null, root),774            msUntilTimeout,775          );776          break;777        }778      }779      // The work expired. Commit immediately.780      commitRoot(root);781      break;782    }783    case RootSuspendedWithDelay: {784      markRootSuspendedAtTime(root, expirationTime);785      const lastSuspendedTime = root.lastSuspendedTime;786      if (expirationTime === lastSuspendedTime) {787        root.nextKnownPendingLevel = getRemainingExpirationTime(finishedWork);788      }789      if (790        // do not delay if we're inside an act() scope791        !(792          true &&793          flushSuspenseFallbacksInTests &&794          IsThisRendererActing.current795        )796      ) {797        // We're suspended in a state that should be avoided. We'll try to798        // avoid committing it for as long as the timeouts let us.799        if (workInProgressRootHasPendingPing) {800          const lastPingedTime = root.lastPingedTime;801          if (lastPingedTime === NoWork || lastPingedTime >= expirationTime) {802            // This render was pinged but we didn't get to restart earlier803            // so try restarting now instead.804            root.lastPingedTime = expirationTime;805            prepareFreshStack(root, expirationTime);806            break;807          }808        }809        const nextTime = getNextRootExpirationTimeToWorkOn(root);810        if (nextTime !== NoWork && nextTime !== expirationTime) {811          // There's additional work on this root.812          break;813        }814        if (815          lastSuspendedTime !== NoWork &&816          lastSuspendedTime !== expirationTime817        ) {818          // We should prefer to render the fallback of at the last819          // suspended level. Ping the last suspended level to try820          // rendering it again.821          root.lastPingedTime = lastSuspendedTime;822          break;823        }824        let msUntilTimeout;825        if (workInProgressRootLatestSuspenseTimeout !== Sync) {826          // We have processed a suspense config whose expiration time we827          // can use as the timeout.828          msUntilTimeout =829            expirationTimeToMs(workInProgressRootLatestSuspenseTimeout) - now();830        } else if (workInProgressRootLatestProcessedExpirationTime === Sync) {831          // This should never normally happen because only new updates832          // cause delayed states, so we should have processed something.833          // However, this could also happen in an offscreen tree.834          msUntilTimeout = 0;835        } else {836          // If we don't have a suspense config, we're going to use a837          // heuristic to determine how long we can suspend.838          const eventTimeMs: number = inferTimeFromExpirationTime(839            workInProgressRootLatestProcessedExpirationTime,840          );841          const currentTimeMs = now();842          const timeUntilExpirationMs =843            expirationTimeToMs(expirationTime) - currentTimeMs;844          let timeElapsed = currentTimeMs - eventTimeMs;845          if (timeElapsed < 0) {846            // We get this wrong some time since we estimate the time.847            timeElapsed = 0;848          }849          msUntilTimeout = jnd(timeElapsed) - timeElapsed;850          // Clamp the timeout to the expiration time. TODO: Once the851          // event time is exact instead of inferred from expiration time852          // we don't need this.853          if (timeUntilExpirationMs < msUntilTimeout) {854            msUntilTimeout = timeUntilExpirationMs;855          }856        }857        // Don't bother with a very short suspense time.858        if (msUntilTimeout > 10) {859          // The render is suspended, it hasn't timed out, and there's no860          // lower priority work to do. Instead of committing the fallback861          // immediately, wait for more data to arrive.862          root.timeoutHandle = scheduleTimeout(863            commitRoot.bind(null, root),864            msUntilTimeout,865          );866          break;867        }868      }869      // The work expired. Commit immediately.870      commitRoot(root);871      break;872    }873    case RootCompleted: {874      // The work completed. Ready to commit.875      if (876        // do not delay if we're inside an act() scope877        !(878          true &&879          flushSuspenseFallbacksInTests &&880          IsThisRendererActing.current881        ) &&882        workInProgressRootLatestProcessedExpirationTime !== Sync &&883        workInProgressRootCanSuspendUsingConfig !== null884      ) {885        // If we have exceeded the minimum loading delay, which probably886        // means we have shown a spinner already, we might have to suspend887        // a bit longer to ensure that the spinner is shown for888        // enough time.889        const msUntilTimeout = computeMsUntilSuspenseLoadingDelay(890          workInProgressRootLatestProcessedExpirationTime,891          expirationTime,892          workInProgressRootCanSuspendUsingConfig,893        );894        if (msUntilTimeout > 10) {895          markRootSuspendedAtTime(root, expirationTime);896          root.timeoutHandle = scheduleTimeout(897            commitRoot.bind(null, root),898            msUntilTimeout,899          );900          break;901        }902      }903      commitRoot(root);904      break;905    }906    default: {907      invariant(false, 'Unknown root exit status.');908    }909  }910}911// This is the entry point for synchronous tasks that don't go912// through Scheduler913function performSyncWorkOnRoot(root) {914  // Check if there's expired work on this root. Otherwise, render at Sync.915  const lastExpiredTime = root.lastExpiredTime;916  const expirationTime = lastExpiredTime !== NoWork ? lastExpiredTime : Sync;917  invariant(918    (executionContext & (RenderContext | CommitContext)) === NoContext,919    'Should not already be working.',920  );921  flushPassiveEffects();922  // If the root or expiration time have changed, throw out the existing stack923  // and prepare a fresh one. Otherwise we'll continue where we left off.924  if (root !== workInProgressRoot || expirationTime !== renderExpirationTime) {925    prepareFreshStack(root, expirationTime); // åå§å workInProgress926    startWorkOnPendingInteractions(root, expirationTime);927  }928  // If we have a work-in-progress fiber, it means there's still work to do929  // in this root.930  if (workInProgress !== null) {931    const prevExecutionContext = executionContext;932    executionContext |= RenderContext;933    const prevDispatcher = pushDispatcher(root);934    const prevInteractions = pushInteractions(root);935    startWorkLoopTimer(workInProgress);936    do {937      try {938        debugger939        workLoopSync();940        break;941      } catch (thrownValue) {942        handleError(root, thrownValue);943      }944    } while (true);945    resetContextDependencies();946    executionContext = prevExecutionContext;947    popDispatcher(prevDispatcher);948    if (enableSchedulerTracing) {949      popInteractions(((prevInteractions: any): Set<Interaction>));950    }951    if (workInProgressRootExitStatus === RootFatalErrored) {952      const fatalError = workInProgressRootFatalError;953      stopInterruptedWorkLoopTimer();954      prepareFreshStack(root, expirationTime);955      markRootSuspendedAtTime(root, expirationTime);956      ensureRootIsScheduled(root);957      throw fatalError;958    }959    if (workInProgress !== null) {960      // This is a sync render, so we should have finished the whole tree.961      invariant(962        false,963        'Cannot commit an incomplete root. This error is likely caused by a ' +964          'bug in React. Please file an issue.',965      );966    } else {967      // We now have a consistent tree. Because this is a sync render, we968      // will commit it even if something suspended.969      stopFinishedWorkLoopTimer();970      root.finishedWork = (root.current.alternate: any);971      root.finishedExpirationTime = expirationTime;972      finishSyncRender(root);973    }974    // Before exiting, make sure there's a callback scheduled for the next975    // pending level.976    ensureRootIsScheduled(root);977  }978  return null;979}980function finishSyncRender(root) {981  // Set this to null to indicate there's no in-progress render.982  workInProgressRoot = null;983  debugger;984  commitRoot(root);985}986export function flushRoot(root: FiberRoot, expirationTime: ExpirationTime) {987  markRootExpiredAtTime(root, expirationTime);988  ensureRootIsScheduled(root);989  if ((executionContext & (RenderContext | CommitContext)) === NoContext) {990    flushSyncCallbackQueue();991  }992}993export function flushDiscreteUpdates() {994  // TODO: Should be able to flush inside batchedUpdates, but not inside `act`.995  // However, `act` uses `batchedUpdates`, so there's no way to distinguish996  // those two cases. Need to fix this before exposing flushDiscreteUpdates997  // as a public API.998  if (999    (executionContext & (BatchedContext | RenderContext | CommitContext)) !==1000    NoContext1001  ) {1002    if (true) {1003      if ((executionContext & RenderContext) !== NoContext) {1004        console.error(1005          'unstable_flushDiscreteUpdates: Cannot flush updates when React is ' +1006            'already rendering.',1007        );1008      }1009    }1010    // We're already rendering, so we can't synchronously flush pending work.1011    // This is probably a nested event dispatch triggered by a lifecycle/effect,1012    // like `el.focus()`. Exit.1013    return;1014  }1015  flushPendingDiscreteUpdates();1016  // If the discrete updates scheduled passive effects, flush them now so that1017  // they fire before the next serial event.1018  flushPassiveEffects();1019}1020export function deferredUpdates<A>(fn: () => A): A {1021  // TODO: Remove in favor of Scheduler.next1022  return runWithPriority(NormalPriority, fn);1023}1024export function syncUpdates<A, B, C, R>(1025  fn: (A, B, C) => R,1026  a: A,1027  b: B,1028  c: C,1029): R {1030  return runWithPriority(ImmediatePriority, fn.bind(null, a, b, c));1031}1032function flushPendingDiscreteUpdates() {1033  if (rootsWithPendingDiscreteUpdates !== null) {1034    // For each root with pending discrete updates, schedule a callback to1035    // immediately flush them.1036    const roots = rootsWithPendingDiscreteUpdates;1037    rootsWithPendingDiscreteUpdates = null;1038    roots.forEach((expirationTime, root) => {1039      markRootExpiredAtTime(root, expirationTime);1040      ensureRootIsScheduled(root);1041    });1042    // Now flush the immediate queue.1043    flushSyncCallbackQueue();1044  }1045}1046export function batchedUpdates<A, R>(fn: A => R, a: A): R {1047  const prevExecutionContext = executionContext;1048  executionContext |= BatchedContext;1049  try {1050    return fn(a);1051  } finally {1052    executionContext = prevExecutionContext;1053    if (executionContext === NoContext) {1054      // Flush the immediate callbacks that were scheduled during this batch1055      flushSyncCallbackQueue();1056    }1057  }1058}1059export function batchedEventUpdates<A, R>(fn: A => R, a: A): R {1060  const prevExecutionContext = executionContext;1061  executionContext |= EventContext;1062  try {1063    return fn(a);1064  } finally {1065    executionContext = prevExecutionContext;1066    if (executionContext === NoContext) {1067      // Flush the immediate callbacks that were scheduled during this batch1068      flushSyncCallbackQueue();1069    }1070  }1071}1072export function discreteUpdates<A, B, C, D, R>(1073  fn: (A, B, C) => R,1074  a: A,1075  b: B,1076  c: C,1077  d: D,1078): R {1079  const prevExecutionContext = executionContext;1080  executionContext |= DiscreteEventContext;1081  try {1082    // Should this1083    return runWithPriority(UserBlockingPriority, fn.bind(null, a, b, c, d));1084  } finally {1085    executionContext = prevExecutionContext;1086    if (executionContext === NoContext) {1087      // Flush the immediate callbacks that were scheduled during this batch1088      flushSyncCallbackQueue();1089    }1090  }1091}1092export function unbatchedUpdates<A, R>(fn: (a: A) => R, a: A): R {1093  const prevExecutionContext = executionContext;1094  executionContext &= ~BatchedContext;1095  executionContext |= LegacyUnbatchedContext;1096  try {1097    return fn(a);1098  } finally {1099    executionContext = prevExecutionContext;1100    if (executionContext === NoContext) {1101      // Flush the immediate callbacks that were scheduled during this batch1102      flushSyncCallbackQueue();1103    }1104  }1105}1106export function flushSync<A, R>(fn: A => R, a: A): R {1107  if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {1108    invariant(1109      false,1110      'flushSync was called from inside a lifecycle method. It cannot be ' +1111        'called when React is already rendering.',1112    );1113  }1114  const prevExecutionContext = executionContext;1115  executionContext |= BatchedContext;1116  try {1117    return runWithPriority(ImmediatePriority, fn.bind(null, a));1118  } finally {1119    executionContext = prevExecutionContext;1120    // Flush the immediate callbacks that were scheduled during this batch.1121    // Note that this will happen even if batchedUpdates is higher up1122    // the stack.1123    flushSyncCallbackQueue();1124  }1125}1126export function flushControlled(fn: () => mixed): void {1127  const prevExecutionContext = executionContext;1128  executionContext |= BatchedContext;1129  try {1130    runWithPriority(ImmediatePriority, fn);1131  } finally {1132    executionContext = prevExecutionContext;1133    if (executionContext === NoContext) {1134      // Flush the immediate callbacks that were scheduled during this batch1135      flushSyncCallbackQueue();1136    }1137  }1138}1139function prepareFreshStack(root, expirationTime) {1140  root.finishedWork = null;1141  root.finishedExpirationTime = NoWork;1142  const timeoutHandle = root.timeoutHandle;1143  if (timeoutHandle !== noTimeout) {1144    // The root previous suspended and scheduled a timeout to commit a fallback1145    // state. Now that we have additional work, cancel the timeout.1146    root.timeoutHandle = noTimeout;1147    // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above1148    cancelTimeout(timeoutHandle);1149  }1150  if (workInProgress !== null) {1151    let interruptedWork = workInProgress.return;1152    while (interruptedWork !== null) {1153      unwindInterruptedWork(interruptedWork);1154      interruptedWork = interruptedWork.return;1155    }1156  }1157  workInProgressRoot = root;1158  debugger1159  workInProgress = createWorkInProgress(root.current, null);1160  renderExpirationTime = expirationTime;1161  workInProgressRootExitStatus = RootIncomplete;1162  workInProgressRootFatalError = null;1163  workInProgressRootLatestProcessedExpirationTime = Sync;1164  workInProgressRootLatestSuspenseTimeout = Sync;1165  workInProgressRootCanSuspendUsingConfig = null;1166  workInProgressRootNextUnprocessedUpdateTime = NoWork;1167  workInProgressRootHasPendingPing = false;1168  if (enableSchedulerTracing) {1169    spawnedWorkDuringRender = null;1170  }1171  if (true) {1172    ReactStrictModeWarnings.discardPendingWarnings();1173  }1174}1175function handleError(root, thrownValue) {1176  do {1177    try {1178      // Reset module-level state that was set during the render phase.1179      resetContextDependencies();1180      resetHooksAfterThrow();1181      resetCurrentDebugFiberInDEV();1182      if (workInProgress === null || workInProgress.return === null) {1183        // Expected to be working on a non-root fiber. This is a fatal error1184        // because there's no ancestor that can handle it; the root is1185        // supposed to capture all errors that weren't caught by an error1186        // boundary.1187        workInProgressRootExitStatus = RootFatalErrored;1188        workInProgressRootFatalError = thrownValue;1189        // Set `workInProgress` to null. This represents advancing to the next1190        // sibling, or the parent if there are no siblings. But since the root1191        // has no siblings nor a parent, we set it to null. Usually this is1192        // handled by `completeUnitOfWork` or `unwindWork`, but since we're1193        // interntionally not calling those, we need set it here.1194        // TODO: Consider calling `unwindWork` to pop the contexts.1195        workInProgress = null;1196        return null;1197      }1198      if (enableProfilerTimer && workInProgress.mode & ProfileMode) {1199        // Record the time spent rendering before an error was thrown. This1200        // avoids inaccurate Profiler durations in the case of a1201        // suspended render.1202        stopProfilerTimerIfRunningAndRecordDelta(workInProgress, true);1203      }1204      throwException(1205        root,1206        workInProgress.return,1207        workInProgress,1208        thrownValue,1209        renderExpirationTime,1210      );1211      workInProgress = completeUnitOfWork(workInProgress);1212    } catch (yetAnotherThrownValue) {1213      // Something in the return path also threw.1214      thrownValue = yetAnotherThrownValue;1215      continue;1216    }1217    // Return to the normal work loop.1218    return;1219  } while (true);1220}1221function pushDispatcher(root) {1222  const prevDispatcher = ReactCurrentDispatcher.current;1223  ReactCurrentDispatcher.current = ContextOnlyDispatcher;1224  if (prevDispatcher === null) {1225    // The React isomorphic package does not include a default dispatcher.1226    // Instead the first renderer will lazily attach one, in order to give1227    // nicer error messages.1228    return ContextOnlyDispatcher;1229  } else {1230    return prevDispatcher;1231  }1232}1233function popDispatcher(prevDispatcher) {1234  ReactCurrentDispatcher.current = prevDispatcher;1235}1236function pushInteractions(root) {1237  if (enableSchedulerTracing) {1238    const prevInteractions: Set<Interaction> | null = __interactionsRef.current;1239    __interactionsRef.current = root.memoizedInteractions;1240    return prevInteractions;1241  }1242  return null;1243}1244function popInteractions(prevInteractions) {1245  if (enableSchedulerTracing) {1246    __interactionsRef.current = prevInteractions;1247  }1248}1249export function markCommitTimeOfFallback() {1250  globalMostRecentFallbackTime = now();1251}1252export function markRenderEventTimeAndConfig(1253  expirationTime: ExpirationTime,1254  suspenseConfig: null | SuspenseConfig,1255): void {1256  if (1257    expirationTime < workInProgressRootLatestProcessedExpirationTime &&1258    expirationTime > Idle1259  ) {1260    workInProgressRootLatestProcessedExpirationTime = expirationTime;1261  }1262  if (suspenseConfig !== null) {1263    if (1264      expirationTime < workInProgressRootLatestSuspenseTimeout &&1265      expirationTime > Idle1266    ) {1267      workInProgressRootLatestSuspenseTimeout = expirationTime;1268      // Most of the time we only have one config and getting wrong is not bad.1269      workInProgressRootCanSuspendUsingConfig = suspenseConfig;1270    }1271  }1272}1273export function markUnprocessedUpdateTime(1274  expirationTime: ExpirationTime,1275): void {1276  if (expirationTime > workInProgressRootNextUnprocessedUpdateTime) {1277    workInProgressRootNextUnprocessedUpdateTime = expirationTime;1278  }1279}1280export function renderDidSuspend(): void {1281  if (workInProgressRootExitStatus === RootIncomplete) {1282    workInProgressRootExitStatus = RootSuspended;1283  }1284}1285export function renderDidSuspendDelayIfPossible(): void {1286  if (1287    workInProgressRootExitStatus === RootIncomplete ||1288    workInProgressRootExitStatus === RootSuspended1289  ) {1290    workInProgressRootExitStatus = RootSuspendedWithDelay;1291  }1292  // Check if there's a lower priority update somewhere else in the tree.1293  if (1294    workInProgressRootNextUnprocessedUpdateTime !== NoWork &&1295    workInProgressRoot !== null1296  ) {1297    // Mark the current render as suspended, and then mark that there's a1298    // pending update.1299    // TODO: This should immediately interrupt the current render, instead1300    // of waiting until the next time we yield.1301    markRootSuspendedAtTime(workInProgressRoot, renderExpirationTime);1302    markRootUpdatedAtTime(1303      workInProgressRoot,1304      workInProgressRootNextUnprocessedUpdateTime,1305    );1306  }1307}1308export function renderDidError() {1309  if (workInProgressRootExitStatus !== RootCompleted) {1310    workInProgressRootExitStatus = RootErrored;1311  }1312}1313// Called during render to determine if anything has suspended.1314// Returns false if we're not sure.1315export function renderHasNotSuspendedYet(): boolean {1316  // If something errored or completed, we can't really be sure,1317  // so those are false.1318  return workInProgressRootExitStatus === RootIncomplete;1319}1320function inferTimeFromExpirationTime(expirationTime: ExpirationTime): number {1321  // We don't know exactly when the update was scheduled, but we can infer an1322  // approximate start time from the expiration time.1323  const earliestExpirationTimeMs = expirationTimeToMs(expirationTime);1324  return earliestExpirationTimeMs - LOW_PRIORITY_EXPIRATION;1325}1326function inferTimeFromExpirationTimeWithSuspenseConfig(1327  expirationTime: ExpirationTime,1328  suspenseConfig: SuspenseConfig,1329): number {1330  // We don't know exactly when the update was scheduled, but we can infer an1331  // approximate start time from the expiration time by subtracting the timeout1332  // that was added to the event time.1333  const earliestExpirationTimeMs = expirationTimeToMs(expirationTime);1334  return (1335    earliestExpirationTimeMs -1336    (suspenseConfig.timeoutMs | 0 || LOW_PRIORITY_EXPIRATION)1337  );1338}1339// The work loop is an extremely hot path. Tell Closure not to inline it.1340/** @noinline */1341function workLoopSync() {1342  // Already timed out, so perform work without checking if we need to yield.1343  while (workInProgress !== null) {1344    debugger1345    workInProgress = performUnitOfWork(workInProgress);1346  }1347}1348/** @noinline */1349function workLoopConcurrent() {1350  // Perform work until Scheduler asks us to yield1351  while (workInProgress !== null && !shouldYield()) {1352    workInProgress = performUnitOfWork(workInProgress);1353  }1354}1355function performUnitOfWork(unitOfWork: Fiber): Fiber | null {1356  // The current, flushed, state of this fiber is the alternate. Ideally1357  // nothing should rely on this, but relying on it here means that we don't1358  // need an additional field on the work in progress.1359  const current = unitOfWork.alternate;1360  1361  startWorkTimer(unitOfWork);1362  setCurrentDebugFiberInDEV(unitOfWork);1363  let next;1364  if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {1365    startProfilerTimer(unitOfWork);1366    debugger;1367    next = beginWork(current, unitOfWork, renderExpirationTime);1368    stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);1369  } else {1370    next = beginWork(current, unitOfWork, renderExpirationTime);1371  }1372  resetCurrentDebugFiberInDEV();1373  unitOfWork.memoizedProps = unitOfWork.pendingProps;1374  if (next === null) {1375    // If this doesn't spawn new work, complete the current work.1376    next = completeUnitOfWork(unitOfWork);1377  }1378  console.log(next)1379  ReactCurrentOwner.current = null;1380  return next;1381}1382function completeUnitOfWork(unitOfWork: Fiber): Fiber | null {1383  // Attempt to complete the current unit of work, then move to the next1384  // sibling. If there are no more siblings, return to the parent fiber.1385  workInProgress = unitOfWork;1386  do {1387    // The current, flushed, state of this fiber is the alternate. Ideally1388    // nothing should rely on this, but relying on it here means that we don't1389    // need an additional field on the work in progress.1390    const current = workInProgress.alternate;1391    const returnFiber = workInProgress.return;1392    // Check if the work completed or if something threw.1393    if ((workInProgress.effectTag & Incomplete) === NoEffect) {1394      setCurrentDebugFiberInDEV(workInProgress);1395      let next;1396      if (1397        !enableProfilerTimer ||1398        (workInProgress.mode & ProfileMode) === NoMode1399      ) {1400        next = completeWork(current, workInProgress, renderExpirationTime);1401      } else {1402        startProfilerTimer(workInProgress);1403        next = completeWork(current, workInProgress, renderExpirationTime);1404        // Update render duration assuming we didn't error.1405        stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);1406      }1407      stopWorkTimer(workInProgress);1408      resetCurrentDebugFiberInDEV();1409      resetChildExpirationTime(workInProgress);1410      if (next !== null) {1411        // Completing this fiber spawned new work. Work on that next.1412        return next;1413      }1414      if (1415        returnFiber !== null &&1416        // Do not append effects to parents if a sibling failed to complete1417        (returnFiber.effectTag & Incomplete) === NoEffect1418      ) {1419        // Append all the effects of the subtree and this fiber onto the effect1420        // list of the parent. The completion order of the children affects the1421        // side-effect order.1422        if (returnFiber.firstEffect === null) {1423          returnFiber.firstEffect = workInProgress.firstEffect;1424        }1425        if (workInProgress.lastEffect !== null) {1426          if (returnFiber.lastEffect !== null) {1427            returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;1428          }1429          returnFiber.lastEffect = workInProgress.lastEffect;1430        }1431        // If this fiber had side-effects, we append it AFTER the children's1432        // side-effects. We can perform certain side-effects earlier if needed,1433        // by doing multiple passes over the effect list. We don't want to1434        // schedule our own side-effect on our own list because if end up1435        // reusing children we'll schedule this effect onto itself since we're1436        // at the end.1437        const effectTag = workInProgress.effectTag;1438        // Skip both NoWork and PerformedWork tags when creating the effect1439        // list. PerformedWork effect is read by React DevTools but shouldn't be1440        // committed.1441        if (effectTag > PerformedWork) {1442          if (returnFiber.lastEffect !== null) {1443            returnFiber.lastEffect.nextEffect = workInProgress;1444          } else {1445            returnFiber.firstEffect = workInProgress;1446          }1447          returnFiber.lastEffect = workInProgress;1448        }1449      }1450    } else {1451      // This fiber did not complete because something threw. Pop values off1452      // the stack without entering the complete phase. If this is a boundary,1453      // capture values if possible.1454      const next = unwindWork(workInProgress, renderExpirationTime);1455      // Because this fiber did not complete, don't reset its expiration time.1456      if (1457        enableProfilerTimer &&1458        (workInProgress.mode & ProfileMode) !== NoMode1459      ) {1460        // Record the render duration for the fiber that errored.1461        stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);1462        // Include the time spent working on failed children before continuing.1463        let actualDuration = workInProgress.actualDuration;1464        let child = workInProgress.child;1465        while (child !== null) {1466          actualDuration += child.actualDuration;1467          child = child.sibling;1468        }1469        workInProgress.actualDuration = actualDuration;1470      }1471      if (next !== null) {1472        // If completing this work spawned new work, do that next. We'll come1473        // back here again.1474        // Since we're restarting, remove anything that is not a host effect1475        // from the effect tag.1476        // TODO: The name stopFailedWorkTimer is misleading because Suspense1477        // also captures and restarts.1478        stopFailedWorkTimer(workInProgress);1479        next.effectTag &= HostEffectMask;1480        return next;1481      }1482      stopWorkTimer(workInProgress);1483      if (returnFiber !== null) {1484        // Mark the parent fiber as incomplete and clear its effect list.1485        returnFiber.firstEffect = returnFiber.lastEffect = null;1486        returnFiber.effectTag |= Incomplete;1487      }1488    }1489    const siblingFiber = workInProgress.sibling;1490    if (siblingFiber !== null) {1491      // If there is more work to do in this returnFiber, do that next.1492      return siblingFiber;1493    }1494    // Otherwise, return to the parent1495    workInProgress = returnFiber;1496  } while (workInProgress !== null);1497  // We've reached the root.1498  if (workInProgressRootExitStatus === RootIncomplete) {1499    workInProgressRootExitStatus = RootCompleted;1500  }1501  return null;1502}1503function getRemainingExpirationTime(fiber: Fiber) {1504  const updateExpirationTime = fiber.expirationTime;1505  const childExpirationTime = fiber.childExpirationTime;1506  return updateExpirationTime > childExpirationTime1507    ? updateExpirationTime1508    : childExpirationTime;1509}1510function resetChildExpirationTime(completedWork: Fiber) {1511  if (1512    renderExpirationTime !== Never &&1513    completedWork.childExpirationTime === Never1514  ) {1515    // The children of this component are hidden. Don't bubble their1516    // expiration times.1517    return;1518  }1519  let newChildExpirationTime = NoWork;1520  // Bubble up the earliest expiration time.1521  if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoMode) {1522    // In profiling mode, resetChildExpirationTime is also used to reset1523    // profiler durations.1524    let actualDuration = completedWork.actualDuration;1525    let treeBaseDuration = completedWork.selfBaseDuration;1526    // When a fiber is cloned, its actualDuration is reset to 0. This value will1527    // only be updated if work is done on the fiber (i.e. it doesn't bailout).1528    // When work is done, it should bubble to the parent's actualDuration. If1529    // the fiber has not been cloned though, (meaning no work was done), then1530    // this value will reflect the amount of time spent working on a previous1531    // render. In that case it should not bubble. We determine whether it was1532    // cloned by comparing the child pointer.1533    const shouldBubbleActualDurations =1534      completedWork.alternate === null ||1535      completedWork.child !== completedWork.alternate.child;1536    let child = completedWork.child;1537    while (child !== null) {1538      const childUpdateExpirationTime = child.expirationTime;1539      const childChildExpirationTime = child.childExpirationTime;1540      if (childUpdateExpirationTime > newChildExpirationTime) {1541        newChildExpirationTime = childUpdateExpirationTime;1542      }1543      if (childChildExpirationTime > newChildExpirationTime) {1544        newChildExpirationTime = childChildExpirationTime;1545      }1546      if (shouldBubbleActualDurations) {1547        actualDuration += child.actualDuration;1548      }1549      treeBaseDuration += child.treeBaseDuration;1550      child = child.sibling;1551    }1552    completedWork.actualDuration = actualDuration;1553    completedWork.treeBaseDuration = treeBaseDuration;1554  } else {1555    let child = completedWork.child;1556    while (child !== null) {1557      const childUpdateExpirationTime = child.expirationTime;1558      const childChildExpirationTime = child.childExpirationTime;1559      if (childUpdateExpirationTime > newChildExpirationTime) {1560        newChildExpirationTime = childUpdateExpirationTime;1561      }1562      if (childChildExpirationTime > newChildExpirationTime) {1563        newChildExpirationTime = childChildExpirationTime;1564      }1565      child = child.sibling;1566    }1567  }1568  completedWork.childExpirationTime = newChildExpirationTime;1569}1570function commitRoot(root) {1571  const renderPriorityLevel = getCurrentPriorityLevel();1572  runWithPriority(1573    ImmediatePriority,1574    commitRootImpl.bind(null, root, renderPriorityLevel),1575  );1576  return null;1577}1578function commitRootImpl(root, renderPriorityLevel) {1579  do {1580    // `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which1581    // means `flushPassiveEffects` will sometimes result in additional1582    // passive effects. So we need to keep flushing in a loop until there are1583    // no more pending effects.1584    // TODO: Might be better if `flushPassiveEffects` did not automatically1585    // flush synchronous work at the end, to avoid factoring hazards like this.1586    debugger1587    flushPassiveEffects();1588  } while (rootWithPendingPassiveEffects !== null);1589  flushRenderPhaseStrictModeWarningsInDEV();1590  invariant(1591    (executionContext & (RenderContext | CommitContext)) === NoContext,1592    'Should not already be working.',1593  );1594  const finishedWork = root.finishedWork;1595  const expirationTime = root.finishedExpirationTime;1596  if (finishedWork === null) {1597    return null;1598  }1599  root.finishedWork = null;1600  root.finishedExpirationTime = NoWork;1601  invariant(1602    finishedWork !== root.current,1603    'Cannot commit the same tree as before. This error is likely caused by ' +1604      'a bug in React. Please file an issue.',1605  );1606  // commitRoot never returns a continuation; it always finishes synchronously.1607  // So we can clear these now to allow a new callback to be scheduled.1608  root.callbackNode = null;1609  root.callbackExpirationTime = NoWork;1610  root.callbackPriority = NoPriority;1611  root.nextKnownPendingLevel = NoWork;1612  startCommitTimer();1613  // Update the first and last pending times on this root. The new first1614  // pending time is whatever is left on the root fiber.1615  const remainingExpirationTimeBeforeCommit = getRemainingExpirationTime(1616    finishedWork,1617  );1618  markRootFinishedAtTime(1619    root,1620    expirationTime,1621    remainingExpirationTimeBeforeCommit,1622  );1623  if (root === workInProgressRoot) {1624    // We can reset these now that they are finished.1625    workInProgressRoot = null;1626    workInProgress = null;1627    renderExpirationTime = NoWork;1628  } else {1629    // This indicates that the last root we worked on is not the same one that1630    // we're committing now. This most commonly happens when a suspended root1631    // times out.1632  }1633  // Get the list of effects.1634  let firstEffect;1635  if (finishedWork.effectTag > PerformedWork) {1636    // A fiber's effect list consists only of its children, not itself. So if1637    // the root has an effect, we need to add it to the end of the list. The1638    // resulting list is the set that would belong to the root's parent, if it1639    // had one; that is, all the effects in the tree including the root.1640    if (finishedWork.lastEffect !== null) {1641      finishedWork.lastEffect.nextEffect = finishedWork;1642      firstEffect = finishedWork.firstEffect;1643    } else {1644      firstEffect = finishedWork;1645    }1646  } else {1647    // There is no effect on the root.1648    firstEffect = finishedWork.firstEffect;1649  }1650  if (firstEffect !== null) { // èµ°è¿é1651    const prevExecutionContext = executionContext;1652    executionContext |= CommitContext;1653    const prevInteractions = pushInteractions(root);1654    // Reset this to null before calling lifecycles1655    ReactCurrentOwner.current = null;1656    // The commit phase is broken into several sub-phases. We do a separate pass1657    // of the effect list for each phase: all mutation effects come before all1658    // layout effects, and so on.1659    // The first phase a "before mutation" phase. We use this phase to read the1660    // state of the host tree right before we mutate it. This is where1661    // getSnapshotBeforeUpdate is called.1662    startCommitSnapshotEffectsTimer();1663    prepareForCommit(root.containerInfo);1664    nextEffect = firstEffect;1665    do {1666      if (false) {1667        1668      } else {1669        try {1670          debugger1671          commitBeforeMutationEffects();1672        } catch (error) {1673          invariant(nextEffect !== null, 'Should be working on an effect.');1674          captureCommitPhaseError(nextEffect, error);1675          nextEffect = nextEffect.nextEffect;1676        }1677      }1678    } while (nextEffect !== null);1679    stopCommitSnapshotEffectsTimer();1680    if (enableProfilerTimer) {1681      // Mark the current commit time to be shared by all Profilers in this1682      // batch. This enables them to be grouped later.1683      recordCommitTime();1684    }1685    // The next phase is the mutation phase, where we mutate the host tree.1686    startCommitHostEffectsTimer();1687    nextEffect = firstEffect;1688    do {1689      if (false) {1690        1691      } else {1692        try {1693          debugger1694          commitMutationEffects(root, renderPriorityLevel);1695        } catch (error) {1696          invariant(nextEffect !== null, 'Should be working on an effect.');1697          captureCommitPhaseError(nextEffect, error);1698          nextEffect = nextEffect.nextEffect;1699        }1700      }1701    } while (nextEffect !== null);1702    stopCommitHostEffectsTimer();1703    resetAfterCommit(root.containerInfo);1704    // The work-in-progress tree is now the current tree. This must come after1705    // the mutation phase, so that the previous tree is still current during1706    // componentWillUnmount, but before the layout phase, so that the finished1707    // work is current during componentDidMount/Update.1708    root.current = finishedWork;1709    // The next phase is the layout phase, where we call effects that read1710    // the host tree after it's been mutated. The idiomatic use case for this is1711    // layout, but class component lifecycles also fire here for legacy reasons.1712    startCommitLifeCyclesTimer();1713    nextEffect = firstEffect;1714    do {1715      if (false) {1716        1717      } else {1718        try {1719          debugger1720          commitLayoutEffects(root, expirationTime);1721        } catch (error) {1722          invariant(nextEffect !== null, 'Should be working on an effect.');1723          captureCommitPhaseError(nextEffect, error);1724          nextEffect = nextEffect.nextEffect;1725        }1726      }1727    } while (nextEffect !== null);1728    stopCommitLifeCyclesTimer();1729    nextEffect = null;1730    // Tell Scheduler to yield at the end of the frame, so the browser has an1731    // opportunity to paint.1732    requestPaint();1733    if (enableSchedulerTracing) {1734      popInteractions(((prevInteractions: any): Set<Interaction>));1735    }1736    executionContext = prevExecutionContext;1737  } else {1738    // No effects.1739    root.current = finishedWork;1740    // Measure these anyway so the flamegraph explicitly shows that there were1741    // no effects.1742    // TODO: Maybe there's a better way to report this.1743    startCommitSnapshotEffectsTimer();1744    stopCommitSnapshotEffectsTimer();1745    if (enableProfilerTimer) {1746      recordCommitTime();1747    }1748    startCommitHostEffectsTimer();1749    stopCommitHostEffectsTimer();1750    startCommitLifeCyclesTimer();1751    stopCommitLifeCyclesTimer();1752  }1753  stopCommitTimer();1754  const rootDidHavePassiveEffects = rootDoesHavePassiveEffects;1755  if (rootDoesHavePassiveEffects) {1756    // This commit has passive effects. Stash a reference to them. But don't1757    // schedule a callback until after flushing layout work.1758    rootDoesHavePassiveEffects = false;1759    rootWithPendingPassiveEffects = root;1760    pendingPassiveEffectsExpirationTime = expirationTime;1761    pendingPassiveEffectsRenderPriority = renderPriorityLevel;1762  } else {1763    // We are done with the effect chain at this point so let's clear the1764    // nextEffect pointers to assist with GC. If we have passive effects, we'll1765    // clear this in flushPassiveEffects.1766    nextEffect = firstEffect;1767    while (nextEffect !== null) {1768      const nextNextEffect = nextEffect.nextEffect;1769      nextEffect.nextEffect = null;1770      nextEffect = nextNextEffect;1771    }1772  }1773  // Check if there's remaining work on this root1774  const remainingExpirationTime = root.firstPendingTime;1775  if (remainingExpirationTime !== NoWork) {1776    if (enableSchedulerTracing) {1777      if (spawnedWorkDuringRender !== null) {1778        const expirationTimes = spawnedWorkDuringRender;1779        spawnedWorkDuringRender = null;1780        for (let i = 0; i < expirationTimes.length; i++) {1781          scheduleInteractions(1782            root,1783            expirationTimes[i],1784            root.memoizedInteractions,1785          );1786        }1787      }1788      schedulePendingInteractions(root, remainingExpirationTime);1789    }1790  } else {1791    // If there's no remaining work, we can clear the set of already failed1792    // error boundaries.1793    legacyErrorBoundariesThatAlreadyFailed = null;1794  }1795  if (enableSchedulerTracing) {1796    if (!rootDidHavePassiveEffects) {1797      // If there are no passive effects, then we can complete the pending interactions.1798      // Otherwise, we'll wait until after the passive effects are flushed.1799      // Wait to do this until after remaining work has been scheduled,1800      // so that we don't prematurely signal complete for interactions when there's e.g. hidden work.1801      finishPendingInteractions(root, expirationTime);1802    }1803  }1804  if (remainingExpirationTime === Sync) {1805    // Count the number of times the root synchronously re-renders without1806    // finishing. If there are too many, it indicates an infinite update loop.1807    if (root === rootWithNestedUpdates) {1808      nestedUpdateCount++;1809    } else {1810      nestedUpdateCount = 0;1811      rootWithNestedUpdates = root;1812    }1813  } else {1814    nestedUpdateCount = 0;1815  }1816  onCommitRoot(finishedWork.stateNode, expirationTime);1817  // Always call this before exiting `commitRoot`, to ensure that any1818  // additional work on this root is scheduled.1819  ensureRootIsScheduled(root);1820  if (hasUncaughtError) {1821    hasUncaughtError = false;1822    const error = firstUncaughtError;1823    firstUncaughtError = null;1824    throw error;1825  }1826  if ((executionContext & LegacyUnbatchedContext) !== NoContext) {1827    // This is a legacy edge case. We just committed the initial mount of1828    // a ReactDOM.render-ed root inside of batchedUpdates. The commit fired1829    // synchronously, but layout updates should be deferred until the end1830    // of the batch.1831    return null;1832  }1833  // If layout work was scheduled, flush it now.1834  flushSyncCallbackQueue();1835  return null;1836}1837function commitBeforeMutationEffects() {1838  while (nextEffect !== null) {1839    const effectTag = nextEffect.effectTag;1840    if ((effectTag & Snapshot) !== NoEffect) {1841      setCurrentDebugFiberInDEV(nextEffect);1842      recordEffect();1843      const current = nextEffect.alternate;1844      commitBeforeMutationEffectOnFiber(current, nextEffect);1845      resetCurrentDebugFiberInDEV();1846    }1847    if ((effectTag & Passive) !== NoEffect) {1848      // If there are passive effects, schedule a callback to flush at1849      // the earliest opportunity.1850      if (!rootDoesHavePassiveEffects) {1851        rootDoesHavePassiveEffects = true;1852        scheduleCallback(NormalPriority, () => {1853          flushPassiveEffects();1854          return null;1855        });1856      }1857    }1858    nextEffect = nextEffect.nextEffect;1859  }1860}1861function commitMutationEffects(root: FiberRoot, renderPriorityLevel) {1862  // TODO: Should probably move the bulk of this function to commitWork.1863  while (nextEffect !== null) {1864    setCurrentDebugFiberInDEV(nextEffect);1865    const effectTag = nextEffect.effectTag;1866    if (effectTag & ContentReset) {1867      commitResetTextContent(nextEffect);1868    }1869    if (effectTag & Ref) {1870      const current = nextEffect.alternate;1871      if (current !== null) {1872        commitDetachRef(current);1873      }1874    }1875    // The following switch statement is only concerned about placement,1876    // updates, and deletions. To avoid needing to add a case for every possible1877    // bitmap value, we remove the secondary effects from the effect tag and1878    // switch on that value.1879    let primaryEffectTag =1880      effectTag & (Placement | Update | Deletion | Hydrating);1881    switch (primaryEffectTag) {1882      case Placement: {1883        commitPlacement(nextEffect);1884        // Clear the "placement" from effect tag so that we know that this is1885        // inserted, before any life-cycles like componentDidMount gets called.1886        // TODO: findDOMNode doesn't rely on this any more but isMounted does1887        // and isMounted is deprecated anyway so we should be able to kill this.1888        nextEffect.effectTag &= ~Placement;1889        break;1890      }1891      case PlacementAndUpdate: {1892        // Placement1893        commitPlacement(nextEffect);1894        // Clear the "placement" from effect tag so that we know that this is1895        // inserted, before any life-cycles like componentDidMount gets called.1896        nextEffect.effectTag &= ~Placement;1897        // Update1898        const current = nextEffect.alternate;1899        commitWork(current, nextEffect);1900        break;1901      }1902      case Hydrating: {1903        nextEffect.effectTag &= ~Hydrating;1904        break;1905      }1906      case HydratingAndUpdate: {1907        nextEffect.effectTag &= ~Hydrating;1908        // Update1909        const current = nextEffect.alternate;1910        commitWork(current, nextEffect);1911        break;1912      }1913      case Update: {1914        const current = nextEffect.alternate;1915        commitWork(current, nextEffect);1916        break;1917      }1918      case Deletion: {1919        commitDeletion(root, nextEffect, renderPriorityLevel);1920        break;1921      }1922    }1923    // TODO: Only record a mutation effect if primaryEffectTag is non-zero.1924    recordEffect();1925    resetCurrentDebugFiberInDEV();1926    nextEffect = nextEffect.nextEffect;1927  }1928}1929function commitLayoutEffects(1930  root: FiberRoot,1931  committedExpirationTime: ExpirationTime,1932) {1933  // TODO: Should probably move the bulk of this function to commitWork.1934  while (nextEffect !== null) {1935    setCurrentDebugFiberInDEV(nextEffect);1936    const effectTag = nextEffect.effectTag;1937    if (effectTag & (Update | Callback)) {1938      recordEffect();1939      const current = nextEffect.alternate;1940      commitLayoutEffectOnFiber(1941        root,1942        current,1943        nextEffect,1944        committedExpirationTime,1945      );1946    }1947    if (effectTag & Ref) {1948      recordEffect();1949      commitAttachRef(nextEffect);1950    }1951    resetCurrentDebugFiberInDEV();1952    nextEffect = nextEffect.nextEffect;1953  }1954}1955export function flushPassiveEffects() {1956  if (pendingPassiveEffectsRenderPriority !== NoPriority) {1957    const priorityLevel =1958      pendingPassiveEffectsRenderPriority > NormalPriority1959        ? NormalPriority1960        : pendingPassiveEffectsRenderPriority;1961    pendingPassiveEffectsRenderPriority = NoPriority;1962    return runWithPriority(priorityLevel, flushPassiveEffectsImpl);1963  }1964}1965export function enqueuePendingPassiveHookEffectMount(1966  fiber: Fiber,1967  effect: HookEffect,1968): void {1969  if (runAllPassiveEffectDestroysBeforeCreates) {1970    pendingPassiveHookEffectsMount.push(effect, fiber);1971    if (!rootDoesHavePassiveEffects) {1972      rootDoesHavePassiveEffects = true;1973      scheduleCallback(NormalPriority, () => {1974        flushPassiveEffects();1975        return null;1976      });1977    }1978  }1979}1980export function enqueuePendingPassiveHookEffectUnmount(1981  fiber: Fiber,1982  effect: HookEffect,1983): void {1984  if (runAllPassiveEffectDestroysBeforeCreates) {1985    pendingPassiveHookEffectsUnmount.push(effect, fiber);1986    if (!rootDoesHavePassiveEffects) {1987      rootDoesHavePassiveEffects = true;1988      scheduleCallback(NormalPriority, () => {1989        flushPassiveEffects();1990        return null;1991      });1992    }1993  }1994}1995function invokePassiveEffectCreate(effect: HookEffect): void {1996  const create = effect.create;1997  effect.destroy = create();1998}1999function flushPassiveEffectsImpl() {2000  if (rootWithPendingPassiveEffects === null) {2001    return false;2002  }2003  const root = rootWithPendingPassiveEffects;2004  const expirationTime = pendingPassiveEffectsExpirationTime;2005  rootWithPendingPassiveEffects = null;2006  pendingPassiveEffectsExpirationTime = NoWork;2007  invariant(2008    (executionContext & (RenderContext | CommitContext)) === NoContext,2009    'Cannot flush passive effects while already rendering.',2010  );2011  const prevExecutionContext = executionContext;2012  executionContext |= CommitContext;2013  const prevInteractions = pushInteractions(root);2014  if (runAllPassiveEffectDestroysBeforeCreates) {2015    // It's important that ALL pending passive effect destroy functions are called2016    // before ANY passive effect create functions are called.2017    // Otherwise effects in sibling components might interfere with each other.2018    // e.g. a destroy function in one component may unintentionally override a ref2019    // value set by a create function in another component.2020    // Layout effects have the same constraint.2021    // First pass: Destroy stale passive effects.2022    let unmountEffects = pendingPassiveHookEffectsUnmount;2023    pendingPassiveHookEffectsUnmount = [];2024    for (let i = 0; i < unmountEffects.length; i += 2) {2025      const effect = ((unmountEffects[i]: any): HookEffect);2026      const fiber = ((unmountEffects[i + 1]: any): Fiber);2027      const destroy = effect.destroy;2028      effect.destroy = undefined;2029      if (typeof destroy === 'function') {2030        if (true) {2031          setCurrentDebugFiberInDEV(fiber);2032          invokeGuardedCallback(null, destroy, null);2033          if (hasCaughtError()) {2034            invariant(fiber !== null, 'Should be working on an effect.');2035            const error = clearCaughtError();2036            captureCommitPhaseError(fiber, error);2037          }2038          resetCurrentDebugFiberInDEV();2039        } else {2040          try {2041            destroy();2042          } catch (error) {2043            invariant(fiber !== null, 'Should be working on an effect.');2044            captureCommitPhaseError(fiber, error);2045          }2046        }2047      }2048    }2049    // Second pass: Create new passive effects.2050    let mountEffects = pendingPassiveHookEffectsMount;2051    pendingPassiveHookEffectsMount = [];2052    for (let i = 0; i < mountEffects.length; i += 2) {2053      const effect = ((mountEffects[i]: any): HookEffect);2054      const fiber = ((mountEffects[i + 1]: any): Fiber);2055      if (true) {2056        setCurrentDebugFiberInDEV(fiber);2057        invokeGuardedCallback(null, invokePassiveEffectCreate, null, effect);2058        if (hasCaughtError()) {2059          invariant(fiber !== null, 'Should be working on an effect.');2060          const error = clearCaughtError();2061          captureCommitPhaseError(fiber, error);2062        }2063        resetCurrentDebugFiberInDEV();2064      } else {2065        try {2066          const create = effect.create;2067          effect.destroy = create();2068        } catch (error) {2069          invariant(fiber !== null, 'Should be working on an effect.');2070          captureCommitPhaseError(fiber, error);2071        }2072      }2073    }2074  } else {2075    // Note: This currently assumes there are no passive effects on the root fiber2076    // because the root is not part of its own effect list.2077    // This could change in the future.2078    let effect = root.current.firstEffect;2079    while (effect !== null) {2080      if (true) {2081        setCurrentDebugFiberInDEV(effect);2082        invokeGuardedCallback(null, commitPassiveHookEffects, null, effect);2083        if (hasCaughtError()) {2084          invariant(effect !== null, 'Should be working on an effect.');2085          const error = clearCaughtError();2086          captureCommitPhaseError(effect, error);2087        }2088        resetCurrentDebugFiberInDEV();2089      } else {2090        try {2091          commitPassiveHookEffects(effect);2092        } catch (error) {2093          invariant(effect !== null, 'Should be working on an effect.');2094          captureCommitPhaseError(effect, error);2095        }2096      }2097      const nextNextEffect = effect.nextEffect;2098      // Remove nextEffect pointer to assist GC2099      effect.nextEffect = null;2100      effect = nextNextEffect;2101    }2102  }2103  if (enableSchedulerTracing) {2104    popInteractions(((prevInteractions: any): Set<Interaction>));2105    finishPendingInteractions(root, expirationTime);2106  }2107  executionContext = prevExecutionContext;2108  flushSyncCallbackQueue();2109  // If additional passive effects were scheduled, increment a counter. If this2110  // exceeds the limit, we'll fire a warning.2111  nestedPassiveUpdateCount =2112    rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;2113  return true;2114}2115export function isAlreadyFailedLegacyErrorBoundary(instance: mixed): boolean {2116  return (2117    legacyErrorBoundariesThatAlreadyFailed !== null &&2118    legacyErrorBoundariesThatAlreadyFailed.has(instance)2119  );2120}2121export function markLegacyErrorBoundaryAsFailed(instance: mixed) {2122  if (legacyErrorBoundariesThatAlreadyFailed === null) {2123    legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);2124  } else {2125    legacyErrorBoundariesThatAlreadyFailed.add(instance);2126  }2127}2128function prepareToThrowUncaughtError(error: mixed) {2129  if (!hasUncaughtError) {2130    hasUncaughtError = true;2131    firstUncaughtError = error;2132  }2133}2134export const onUncaughtError = prepareToThrowUncaughtError;2135function captureCommitPhaseErrorOnRoot(2136  rootFiber: Fiber,2137  sourceFiber: Fiber,2138  error: mixed,2139) {2140  const errorInfo = createCapturedValue(error, sourceFiber);2141  const update = createRootErrorUpdate(rootFiber, errorInfo, Sync);2142  enqueueUpdate(rootFiber, update);2143  const root = markUpdateTimeFromFiberToRoot(rootFiber, Sync);2144  if (root !== null) {2145    ensureRootIsScheduled(root);2146    schedulePendingInteractions(root, Sync);2147  }2148}2149export function captureCommitPhaseError(sourceFiber: Fiber, error: mixed) {2150  if (sourceFiber.tag === HostRoot) {2151    // Error was thrown at the root. There is no parent, so the root2152    // itself should capture it.2153    captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);2154    return;2155  }2156  let fiber = sourceFiber.return;2157  while (fiber !== null) {2158    if (fiber.tag === HostRoot) {2159      captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);2160      return;2161    } else if (fiber.tag === ClassComponent) {2162      const ctor = fiber.type;2163      const instance = fiber.stateNode;2164      if (2165        typeof ctor.getDerivedStateFromError === 'function' ||2166        (typeof instance.componentDidCatch === 'function' &&2167          !isAlreadyFailedLegacyErrorBoundary(instance))2168      ) {2169        const errorInfo = createCapturedValue(error, sourceFiber);2170        const update = createClassErrorUpdate(2171          fiber,2172          errorInfo,2173          // TODO: This is always sync2174          Sync,2175        );2176        enqueueUpdate(fiber, update);2177        const root = markUpdateTimeFromFiberToRoot(fiber, Sync);2178        if (root !== null) {2179          ensureRootIsScheduled(root);2180          schedulePendingInteractions(root, Sync);2181        }2182        return;2183      }2184    }2185    fiber = fiber.return;2186  }2187}2188export function pingSuspendedRoot(2189  root: FiberRoot,2190  thenable: Thenable,2191  suspendedTime: ExpirationTime,2192) {2193  const pingCache = root.pingCache;2194  if (pingCache !== null) {2195    // The thenable resolved, so we no longer need to memoize, because it will2196    // never be thrown again.2197    pingCache.delete(thenable);2198  }2199  if (workInProgressRoot === root && renderExpirationTime === suspendedTime) {2200    // Received a ping at the same priority level at which we're currently2201    // rendering. We might want to restart this render. This should mirror2202    // the logic of whether or not a root suspends once it completes.2203    // TODO: If we're rendering sync either due to Sync, Batched or expired,2204    // we should probably never restart.2205    // If we're suspended with delay, we'll always suspend so we can always2206    // restart. If we're suspended without any updates, it might be a retry.2207    // If it's early in the retry we can restart. We can't know for sure2208    // whether we'll eventually process an update during this render pass,2209    // but it's somewhat unlikely that we get to a ping before that, since2210    // getting to the root most update is usually very fast.2211    if (2212      workInProgressRootExitStatus === RootSuspendedWithDelay ||2213      (workInProgressRootExitStatus === RootSuspended &&2214        workInProgressRootLatestProcessedExpirationTime === Sync &&2215        now() - globalMostRecentFallbackTime < FALLBACK_THROTTLE_MS)2216    ) {2217      // Restart from the root. Don't need to schedule a ping because2218      // we're already working on this tree.2219      prepareFreshStack(root, renderExpirationTime);2220    } else {2221      // Even though we can't restart right now, we might get an2222      // opportunity later. So we mark this render as having a ping.2223      workInProgressRootHasPendingPing = true;2224    }2225    return;2226  }2227  if (!isRootSuspendedAtTime(root, suspendedTime)) {2228    // The root is no longer suspended at this time.2229    return;2230  }2231  const lastPingedTime = root.lastPingedTime;2232  if (lastPingedTime !== NoWork && lastPingedTime < suspendedTime) {2233    // There's already a lower priority ping scheduled.2234    return;2235  }2236  // Mark the time at which this ping was scheduled.2237  root.lastPingedTime = suspendedTime;2238  if (!enableTrainModelFix && root.finishedExpirationTime === suspendedTime) {2239    // If there's a pending fallback waiting to commit, throw it away.2240    root.finishedExpirationTime = NoWork;2241    root.finishedWork = null;2242  }2243  ensureRootIsScheduled(root);2244  schedulePendingInteractions(root, suspendedTime);2245}2246function retryTimedOutBoundary(2247  boundaryFiber: Fiber,2248  retryTime: ExpirationTime,2249) {2250  // The boundary fiber (a Suspense component or SuspenseList component)2251  // previously was rendered in its fallback state. One of the promises that2252  // suspended it has resolved, which means at least part of the tree was2253  // likely unblocked. Try rendering again, at a new expiration time.2254  if (retryTime === NoWork) {2255    const suspenseConfig = null; // Retries don't carry over the already committed update.2256    const currentTime = requestCurrentTimeForUpdate();2257    retryTime = computeExpirationForFiber(2258      currentTime,2259      boundaryFiber,2260      suspenseConfig,2261    );2262  }2263  // TODO: Special case idle priority?2264  const root = markUpdateTimeFromFiberToRoot(boundaryFiber, retryTime);2265  if (root !== null) {2266    ensureRootIsScheduled(root);2267    schedulePendingInteractions(root, retryTime);2268  }2269}2270export function retryDehydratedSuspenseBoundary(boundaryFiber: Fiber) {2271  const suspenseState: null | SuspenseState = boundaryFiber.memoizedState;2272  let retryTime = NoWork;2273  if (suspenseState !== null) {2274    retryTime = suspenseState.retryTime;2275  }2276  retryTimedOutBoundary(boundaryFiber, retryTime);2277}2278export function resolveRetryThenable(boundaryFiber: Fiber, thenable: Thenable) {2279  let retryTime = NoWork; // Default2280  let retryCache: WeakSet<Thenable> | Set<Thenable> | null;2281  if (enableSuspenseServerRenderer) {2282    switch (boundaryFiber.tag) {2283      case SuspenseComponent:2284        retryCache = boundaryFiber.stateNode;2285        const suspenseState: null | SuspenseState = boundaryFiber.memoizedState;2286        if (suspenseState !== null) {2287          retryTime = suspenseState.retryTime;2288        }2289        break;2290      case SuspenseListComponent:2291        retryCache = boundaryFiber.stateNode;2292        break;2293      default:2294        invariant(2295          false,2296          'Pinged unknown suspense boundary type. ' +2297            'This is probably a bug in React.',2298        );2299    }2300  } else {2301    retryCache = boundaryFiber.stateNode;2302  }2303  if (retryCache !== null) {2304    // The thenable resolved, so we no longer need to memoize, because it will2305    // never be thrown again.2306    retryCache.delete(thenable);2307  }2308  retryTimedOutBoundary(boundaryFiber, retryTime);2309}2310// Computes the next Just Noticeable Difference (JND) boundary.2311// The theory is that a person can't tell the difference between small differences in time.2312// Therefore, if we wait a bit longer than necessary that won't translate to a noticeable2313// difference in the experience. However, waiting for longer might mean that we can avoid2314// showing an intermediate loading state. The longer we have already waited, the harder it2315// is to tell small differences in time. Therefore, the longer we've already waited,2316// the longer we can wait additionally. At some point we have to give up though.2317// We pick a train model where the next boundary commits at a consistent schedule.2318// These particular numbers are vague estimates. We expect to adjust them based on research.2319function jnd(timeElapsed: number) {2320  return timeElapsed < 1202321    ? 1202322    : timeElapsed < 4802323    ? 4802324    : timeElapsed < 10802325    ? 10802326    : timeElapsed < 19202327    ? 19202328    : timeElapsed < 30002329    ? 30002330    : timeElapsed < 43202331    ? 43202332    : ceil(timeElapsed / 1960) * 1960;2333}2334function computeMsUntilSuspenseLoadingDelay(2335  mostRecentEventTime: ExpirationTime,2336  committedExpirationTime: ExpirationTime,2337  suspenseConfig: SuspenseConfig,2338) {2339  const busyMinDurationMs = (suspenseConfig.busyMinDurationMs: any) | 0;2340  if (busyMinDurationMs <= 0) {2341    return 0;2342  }2343  const busyDelayMs = (suspenseConfig.busyDelayMs: any) | 0;2344  // Compute the time until this render pass would expire.2345  const currentTimeMs: number = now();2346  const eventTimeMs: number = inferTimeFromExpirationTimeWithSuspenseConfig(2347    mostRecentEventTime,2348    suspenseConfig,2349  );2350  const timeElapsed = currentTimeMs - eventTimeMs;2351  if (timeElapsed <= busyDelayMs) {2352    // If we haven't yet waited longer than the initial delay, we don't2353    // have to wait any additional time.2354    return 0;2355  }2356  const msUntilTimeout = busyDelayMs + busyMinDurationMs - timeElapsed;2357  // This is the value that is passed to `setTimeout`.2358  return msUntilTimeout;2359}2360function checkForNestedUpdates() {2361  if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {2362    nestedUpdateCount = 0;2363    rootWithNestedUpdates = null;2364    invariant(2365      false,2366      'Maximum update depth exceeded. This can happen when a component ' +2367        'repeatedly calls setState inside componentWillUpdate or ' +2368        'componentDidUpdate. React limits the number of nested updates to ' +2369        'prevent infinite loops.',2370    );2371  }2372}2373function flushRenderPhaseStrictModeWarningsInDEV() {2374  if (true) {2375    ReactStrictModeWarnings.flushLegacyContextWarning();2376    if (warnAboutDeprecatedLifecycles) {2377      ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings();2378    }2379  }2380}2381function stopFinishedWorkLoopTimer() {2382  const didCompleteRoot = true;2383  stopWorkLoopTimer(interruptedBy, didCompleteRoot);2384  interruptedBy = null;2385}2386function stopInterruptedWorkLoopTimer() {2387  // TODO: Track which fiber caused the interruption.2388  const didCompleteRoot = false;2389  stopWorkLoopTimer(interruptedBy, didCompleteRoot);2390  interruptedBy = null;2391}2392function checkForInterruption(2393  fiberThatReceivedUpdate: Fiber,2394  updateExpirationTime: ExpirationTime,2395) {2396  if (2397    enableUserTimingAPI &&2398    workInProgressRoot !== null &&2399    updateExpirationTime > renderExpirationTime2400  ) {2401    interruptedBy = fiberThatReceivedUpdate;2402  }2403}2404let didWarnStateUpdateForUnmountedComponent: Set<string> | null = null;2405function warnAboutUpdateOnUnmountedFiberInDEV(fiber) {2406  if (true) {...ReactFiberScheduler.js
Source:ReactFiberScheduler.js  
...292    warnAboutUpdateOnUnmountedFiberInDEV(fiber);293    return;294  }295  root.pingTime = NoWork;296  checkForInterruption(fiber, expirationTime);297  recordScheduleUpdate();298  if (expirationTime === Sync) {299    if (workPhase === LegacyUnbatchedPhase) {300      // This is a legacy edge case. The initial mount of a ReactDOM.render-ed301      // root inside of batchedUpdates should be synchronous, but layout updates302      // should be deferred until the end of the batch.303      let callback = renderRoot(root, Sync, true);304      while (callback !== null) {305        callback = callback(true);306      }307    } else {308      scheduleCallbackForRoot(root, ImmediatePriority, Sync);309      if (workPhase === NotWorking) {310        // Flush the synchronous work now, wnless we're already working or inside311        // a batch. This is intentionally inside scheduleUpdateOnFiber instead of312        // scheduleCallbackForFiber to preserve the ability to schedule a callback313        // without immediately flushing it. We only do this for user-initated314        // updates, to preserve historical behavior of sync mode.315        flushImmediateQueue();316      }317    }318  } else {319    // TODO: computeExpirationForFiber also reads the priority. Pass the320    // priority as an argument to that function and this one.321    const priorityLevel = getCurrentPriorityLevel();322    if (priorityLevel === UserBlockingPriority) {323      // This is the result of a discrete event. Track the lowest priority324      // discrete update per root so we can flush them early, if needed.325      if (rootsWithPendingDiscreteUpdates === null) {326        rootsWithPendingDiscreteUpdates = new Map([[root, expirationTime]]);327      } else {328        const lastDiscreteTime = rootsWithPendingDiscreteUpdates.get(root);329        if (330          lastDiscreteTime === undefined ||331          lastDiscreteTime > expirationTime332        ) {333          rootsWithPendingDiscreteUpdates.set(root, expirationTime);334        }335      }336    }337    scheduleCallbackForRoot(root, priorityLevel, expirationTime);338  }339}340export const scheduleWork = scheduleUpdateOnFiber;341// This is split into a separate function so we can mark a fiber with pending342// work without treating it as a typical update that originates from an event;343// e.g. retrying a Suspense boundary isn't an update, but it does schedule work344// on a fiber.345function markUpdateTimeFromFiberToRoot(fiber, expirationTime) {346  // Update the source fiber's expiration time347  if (fiber.expirationTime < expirationTime) {348    fiber.expirationTime = expirationTime;349  }350  let alternate = fiber.alternate;351  if (alternate !== null && alternate.expirationTime < expirationTime) {352    alternate.expirationTime = expirationTime;353  }354  // Walk the parent path to the root and update the child expiration time.355  let node = fiber.return;356  let root = null;357  if (node === null && fiber.tag === HostRoot) {358    root = fiber.stateNode;359  } else {360    while (node !== null) {361      alternate = node.alternate;362      if (node.childExpirationTime < expirationTime) {363        node.childExpirationTime = expirationTime;364        if (365          alternate !== null &&366          alternate.childExpirationTime < expirationTime367        ) {368          alternate.childExpirationTime = expirationTime;369        }370      } else if (371        alternate !== null &&372        alternate.childExpirationTime < expirationTime373      ) {374        alternate.childExpirationTime = expirationTime;375      }376      if (node.return === null && node.tag === HostRoot) {377        root = node.stateNode;378        break;379      }380      node = node.return;381    }382  }383  if (root !== null) {384    // Update the first and last pending expiration times in this root385    const firstPendingTime = root.firstPendingTime;386    if (expirationTime > firstPendingTime) {387      root.firstPendingTime = expirationTime;388    }389    const lastPendingTime = root.lastPendingTime;390    if (lastPendingTime === NoWork || expirationTime < lastPendingTime) {391      root.lastPendingTime = expirationTime;392    }393  }394  return root;395}396// Use this function, along with runRootCallback, to ensure that only a single397// callback per root is scheduled. It's still possible to call renderRoot398// directly, but scheduling via this function helps avoid excessive callbacks.399// It works by storing the callback node and expiration time on the root. When a400// new callback comes in, it compares the expiration time to determine if it401// should cancel the previous one. It also relies on commitRoot scheduling a402// callback to render the next level, because that means we don't need a403// separate callback per expiration time.404function scheduleCallbackForRoot(405  root: FiberRoot,406  priorityLevel: ReactPriorityLevel,407  expirationTime: ExpirationTime,408) {409  const existingCallbackExpirationTime = root.callbackExpirationTime;410  if (existingCallbackExpirationTime < expirationTime) {411    // New callback has higher priority than the existing one.412    const existingCallbackNode = root.callbackNode;413    if (existingCallbackNode !== null) {414      cancelCallback(existingCallbackNode);415    }416    root.callbackExpirationTime = expirationTime;417    let options = null;418    if (expirationTime !== Sync && expirationTime !== Never) {419      let timeout = expirationTimeToMs(expirationTime) - now();420      if (timeout > 5000) {421        // Sanity check. Should never take longer than 5 seconds.422        // TODO: Add internal warning?423        timeout = 5000;424      }425      options = {timeout};426    }427    root.callbackNode = scheduleCallback(428      priorityLevel,429      runRootCallback.bind(430        null,431        root,432        renderRoot.bind(null, root, expirationTime),433      ),434      options,435    );436    if (437      enableUserTimingAPI &&438      expirationTime !== Sync &&439      workPhase !== RenderPhase &&440      workPhase !== CommitPhase441    ) {442      // Scheduled an async callback, and we're not already working. Add an443      // entry to the flamegraph that shows we're waiting for a callback444      // to fire.445      startRequestCallbackTimer();446    }447  }448  // Add the current set of interactions to the pending set associated with449  // this root.450  schedulePendingInteraction(root, expirationTime);451}452function runRootCallback(root, callback, isSync) {453  const prevCallbackNode = root.callbackNode;454  let continuation = null;455  try {456    continuation = callback(isSync);457    if (continuation !== null) {458      return runRootCallback.bind(null, root, continuation);459    } else {460      return null;461    }462  } finally {463    // If the callback exits without returning a continuation, remove the464    // corresponding callback node from the root. Unless the callback node465    // has changed, which implies that it was already cancelled by a high466    // priority update.467    if (continuation === null && prevCallbackNode === root.callbackNode) {468      root.callbackNode = null;469      root.callbackExpirationTime = NoWork;470    }471  }472}473export function flushRoot(root: FiberRoot, expirationTime: ExpirationTime) {474  if (workPhase === RenderPhase || workPhase === CommitPhase) {475    invariant(476      false,477      'work.commit(): Cannot commit while already rendering. This likely ' +478        'means you attempted to commit from inside a lifecycle method.',479    );480  }481  scheduleCallback(482    ImmediatePriority,483    renderRoot.bind(null, root, expirationTime),484  );485  flushImmediateQueue();486}487export function flushInteractiveUpdates() {488  if (workPhase === RenderPhase || workPhase === CommitPhase) {489    // Can't synchronously flush interactive updates if React is already490    // working. This is currently a no-op.491    // TODO: Should we fire a warning? This happens if you synchronously invoke492    // an input event inside an effect, like with `element.click()`.493    return;494  }495  flushPendingDiscreteUpdates();496}497function resolveLocksOnRoot(root: FiberRoot, expirationTime: ExpirationTime) {498  const firstBatch = root.firstBatch;499  if (500    firstBatch !== null &&501    firstBatch._defer &&502    firstBatch._expirationTime >= expirationTime503  ) {504    root.finishedWork = root.current.alternate;505    root.pendingCommitExpirationTime = expirationTime;506    scheduleCallback(NormalPriority, () => {507      firstBatch._onComplete();508      return null;509    });510    return true;511  } else {512    return false;513  }514}515export function deferredUpdates<A>(fn: () => A): A {516  // TODO: Remove in favor of Scheduler.next517  return runWithPriority(NormalPriority, fn);518}519export function interactiveUpdates<A, B, C, R>(520  fn: (A, B, C) => R,521  a: A,522  b: B,523  c: C,524): R {525  if (workPhase === NotWorking) {526    // TODO: Remove this call. Instead of doing this automatically, the caller527    // should explicitly call flushInteractiveUpdates.528    flushPendingDiscreteUpdates();529  }530  return runWithPriority(UserBlockingPriority, fn.bind(null, a, b, c));531}532export function syncUpdates<A, B, C, R>(533  fn: (A, B, C) => R,534  a: A,535  b: B,536  c: C,537): R {538  return runWithPriority(ImmediatePriority, fn.bind(null, a, b, c));539}540function flushPendingDiscreteUpdates() {541  if (rootsWithPendingDiscreteUpdates !== null) {542    // For each root with pending discrete updates, schedule a callback to543    // immediately flush them.544    const roots = rootsWithPendingDiscreteUpdates;545    rootsWithPendingDiscreteUpdates = null;546    roots.forEach((expirationTime, root) => {547      scheduleCallback(548        ImmediatePriority,549        renderRoot.bind(null, root, expirationTime),550      );551    });552    // Now flush the immediate queue.553    flushImmediateQueue();554  }555}556export function batchedUpdates<A, R>(fn: A => R, a: A): R {557  if (workPhase !== NotWorking) {558    // We're already working, or inside a batch, so batchedUpdates is a no-op.559    return fn(a);560  }561  workPhase = BatchedPhase;562  try {563    return fn(a);564  } finally {565    workPhase = NotWorking;566    // Flush the immediate callbacks that were scheduled during this batch567    flushImmediateQueue();568  }569}570export function unbatchedUpdates<A, R>(fn: (a: A) => R, a: A): R {571  if (workPhase !== BatchedPhase && workPhase !== FlushSyncPhase) {572    // We're not inside batchedUpdates or flushSync, so unbatchedUpdates is573    // a no-op.574    return fn(a);575  }576  const prevWorkPhase = workPhase;577  workPhase = LegacyUnbatchedPhase;578  try {579    return fn(a);580  } finally {581    workPhase = prevWorkPhase;582  }583}584export function flushSync<A, R>(fn: A => R, a: A): R {585  if (workPhase === RenderPhase || workPhase === CommitPhase) {586    invariant(587      false,588      'flushSync was called from inside a lifecycle method. It cannot be ' +589        'called when React is already rendering.',590    );591  }592  const prevWorkPhase = workPhase;593  workPhase = FlushSyncPhase;594  try {595    return runWithPriority(ImmediatePriority, fn.bind(null, a));596  } finally {597    workPhase = prevWorkPhase;598    // Flush the immediate callbacks that were scheduled during this batch.599    // Note that this will happen even if batchedUpdates is higher up600    // the stack.601    flushImmediateQueue();602  }603}604export function flushControlled(fn: () => mixed): void {605  const prevWorkPhase = workPhase;606  workPhase = BatchedPhase;607  try {608    runWithPriority(ImmediatePriority, fn);609  } finally {610    workPhase = prevWorkPhase;611    if (workPhase === NotWorking) {612      // Flush the immediate callbacks that were scheduled during this batch613      flushImmediateQueue();614    }615  }616}617function prepareFreshStack(root, expirationTime) {618  root.pendingCommitExpirationTime = NoWork;619  const timeoutHandle = root.timeoutHandle;620  if (timeoutHandle !== noTimeout) {621    // The root previous suspended and scheduled a timeout to commit a fallback622    // state. Now that we have additional work, cancel the timeout.623    root.timeoutHandle = noTimeout;624    // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above625    cancelTimeout(timeoutHandle);626  }627  if (workInProgress !== null) {628    let interruptedWork = workInProgress.return;629    while (interruptedWork !== null) {630      unwindInterruptedWork(interruptedWork);631      interruptedWork = interruptedWork.return;632    }633  }634  workInProgressRoot = root;635  workInProgress = createWorkInProgress(root.current, null, expirationTime);636  renderExpirationTime = expirationTime;637  workInProgressRootExitStatus = RootIncomplete;638  workInProgressRootMostRecentEventTime = Sync;639  if (__DEV__) {640    ReactStrictModeWarnings.discardPendingWarnings();641  }642}643function renderRoot(644  root: FiberRoot,645  expirationTime: ExpirationTime,646  isSync: boolean,647): SchedulerCallback | null {648  invariant(649    workPhase !== RenderPhase && workPhase !== CommitPhase,650    'Should not already be working.',651  );652  if (enableUserTimingAPI && expirationTime !== Sync) {653    const didExpire = isSync;654    stopRequestCallbackTimer(didExpire);655  }656  if (root.firstPendingTime < expirationTime) {657    // If there's no work left at this expiration time, exit immediately. This658    // happens when multiple callbacks are scheduled for a single root, but an659    // earlier callback flushes the work of a later one.660    return null;661  }662  if (root.pendingCommitExpirationTime === expirationTime) {663    // There's already a pending commit at this expiration time.664    root.pendingCommitExpirationTime = NoWork;665    return commitRoot.bind(null, root, expirationTime);666  }667  flushPassiveEffects();668  // If the root or expiration time have changed, throw out the existing stack669  // and prepare a fresh one. Otherwise we'll continue where we left off.670  if (root !== workInProgressRoot || expirationTime !== renderExpirationTime) {671    prepareFreshStack(root, expirationTime);672    startWorkOnPendingInteraction(root, expirationTime);673  }674  // If we have a work-in-progress fiber, it means there's still work to do675  // in this root.676  if (workInProgress !== null) {677    const prevWorkPhase = workPhase;678    workPhase = RenderPhase;679    let prevDispatcher = ReactCurrentDispatcher.current;680    if (prevDispatcher === null) {681      // The React isomorphic package does not include a default dispatcher.682      // Instead the first renderer will lazily attach one, in order to give683      // nicer error messages.684      prevDispatcher = ContextOnlyDispatcher;685    }686    ReactCurrentDispatcher.current = ContextOnlyDispatcher;687    let prevInteractions: Set<Interaction> | null = null;688    if (enableSchedulerTracing) {689      prevInteractions = __interactionsRef.current;690      __interactionsRef.current = root.memoizedInteractions;691    }692    startWorkLoopTimer(workInProgress);693    // TODO: Fork renderRoot into renderRootSync and renderRootAsync694    if (isSync) {695      if (expirationTime !== Sync) {696        // An async update expired. There may be other expired updates on697        // this root. We should render all the expired work in a698        // single batch.699        const currentTime = requestCurrentTime();700        if (currentTime < expirationTime) {701          // Restart at the current time.702          workPhase = prevWorkPhase;703          resetContextDependencies();704          ReactCurrentDispatcher.current = prevDispatcher;705          if (enableSchedulerTracing) {706            __interactionsRef.current = ((prevInteractions: any): Set<707              Interaction,708            >);709          }710          return renderRoot.bind(null, root, currentTime);711        }712      }713    } else {714      // Since we know we're in a React event, we can clear the current715      // event time. The next update will compute a new event time.716      currentEventTime = NoWork;717    }718    do {719      try {720        if (isSync) {721          workLoopSync();722        } else {723          workLoop();724        }725        break;726      } catch (thrownValue) {727        // Reset module-level state that was set during the render phase.728        resetContextDependencies();729        resetHooks();730        const sourceFiber = workInProgress;731        if (sourceFiber === null || sourceFiber.return === null) {732          // Expected to be working on a non-root fiber. This is a fatal error733          // because there's no ancestor that can handle it; the root is734          // supposed to capture all errors that weren't caught by an error735          // boundary.736          prepareFreshStack(root, expirationTime);737          workPhase = prevWorkPhase;738          throw thrownValue;739        }740        if (enableProfilerTimer && sourceFiber.mode & ProfileMode) {741          // Record the time spent rendering before an error was thrown. This742          // avoids inaccurate Profiler durations in the case of a743          // suspended render.744          stopProfilerTimerIfRunningAndRecordDelta(sourceFiber, true);745        }746        const returnFiber = sourceFiber.return;747        throwException(748          root,749          returnFiber,750          sourceFiber,751          thrownValue,752          renderExpirationTime,753        );754        workInProgress = completeUnitOfWork(sourceFiber);755      }756    } while (true);757    workPhase = prevWorkPhase;758    resetContextDependencies();759    ReactCurrentDispatcher.current = prevDispatcher;760    if (enableSchedulerTracing) {761      __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);762    }763    if (workInProgress !== null) {764      // There's still work left over. Return a continuation.765      stopInterruptedWorkLoopTimer();766      if (expirationTime !== Sync) {767        startRequestCallbackTimer();768      }769      return renderRoot.bind(null, root, expirationTime);770    }771  }772  // We now have a consistent tree. The next step is either to commit it, or, if773  // something suspended, wait to commit it after a timeout.774  stopFinishedWorkLoopTimer();775  const isLocked = resolveLocksOnRoot(root, expirationTime);776  if (isLocked) {777    // This root has a lock that prevents it from committing. Exit. If we begin778    // work on the root again, without any intervening updates, it will finish779    // without doing additional work.780    return null;781  }782  // Set this to null to indicate there's no in-progress render.783  workInProgressRoot = null;784  switch (workInProgressRootExitStatus) {785    case RootIncomplete: {786      invariant(false, 'Should have a work-in-progress.');787    }788    // Flow knows about invariant, so it compains if I add a break statement,789    // but eslint doesn't know about invariant, so it complains if I do.790    // eslint-disable-next-line no-fallthrough791    case RootErrored: {792      // An error was thrown. First check if there is lower priority work793      // scheduled on this root.794      const lastPendingTime = root.lastPendingTime;795      if (root.lastPendingTime < expirationTime) {796        // There's lower priority work. Before raising the error, try rendering797        // at the lower priority to see if it fixes it. Use a continuation to798        // maintain the existing priority and position in the queue.799        return renderRoot.bind(null, root, lastPendingTime);800      }801      if (!isSync) {802        // If we're rendering asynchronously, it's possible the error was803        // caused by tearing due to a mutation during an event. Try rendering804        // one more time without yiedling to events.805        prepareFreshStack(root, expirationTime);806        scheduleCallback(807          ImmediatePriority,808          renderRoot.bind(null, root, expirationTime),809        );810        return null;811      }812      // If we're already rendering synchronously, commit the root in its813      // errored state.814      return commitRoot.bind(null, root, expirationTime);815    }816    case RootSuspended: {817      if (!isSync) {818        const lastPendingTime = root.lastPendingTime;819        if (root.lastPendingTime < expirationTime) {820          // There's lower priority work. It might be unsuspended. Try rendering821          // at that level.822          return renderRoot.bind(null, root, lastPendingTime);823        }824        // If workInProgressRootMostRecentEventTime is Sync, that means we didn't825        // track any event times. That can happen if we retried but nothing switched826        // from fallback to content. There's no reason to delay doing no work.827        if (workInProgressRootMostRecentEventTime !== Sync) {828          let msUntilTimeout = computeMsUntilTimeout(829            workInProgressRootMostRecentEventTime,830            expirationTime,831          );832          // Don't bother with a very short suspense time.833          if (msUntilTimeout > 10) {834            // The render is suspended, it hasn't timed out, and there's no lower835            // priority work to do. Instead of committing the fallback836            // immediately, wait for more data to arrive.837            root.timeoutHandle = scheduleTimeout(838              commitRoot.bind(null, root, expirationTime),839              msUntilTimeout,840            );841            return null;842          }843        }844      }845      // The work expired. Commit immediately.846      return commitRoot.bind(null, root, expirationTime);847    }848    case RootCompleted: {849      // The work completed. Ready to commit.850      return commitRoot.bind(null, root, expirationTime);851    }852    default: {853      invariant(false, 'Unknown root exit status.');854    }855  }856}857export function markRenderEventTime(expirationTime: ExpirationTime): void {858  if (expirationTime < workInProgressRootMostRecentEventTime) {859    workInProgressRootMostRecentEventTime = expirationTime;860  }861}862export function renderDidSuspend(): void {863  if (workInProgressRootExitStatus === RootIncomplete) {864    workInProgressRootExitStatus = RootSuspended;865  }866}867export function renderDidError() {868  if (869    workInProgressRootExitStatus === RootIncomplete ||870    workInProgressRootExitStatus === RootSuspended871  ) {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;1701}1702function checkForNestedUpdates() {1703  if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {1704    nestedUpdateCount = 0;1705    rootWithNestedUpdates = null;1706    invariant(1707      false,1708      'Maximum update depth exceeded. This can happen when a component ' +1709        'repeatedly calls setState inside componentWillUpdate or ' +1710        'componentDidUpdate. React limits the number of nested updates to ' +1711        'prevent infinite loops.',1712    );1713  }1714  if (__DEV__) {1715    if (nestedPassiveUpdateCount > NESTED_PASSIVE_UPDATE_LIMIT) {1716      nestedPassiveUpdateCount = 0;1717      warning(1718        false,1719        'Maximum update depth exceeded. This can happen when a component ' +1720          "calls setState inside useEffect, but useEffect either doesn't " +1721          'have a dependency array, or one of the dependencies changes on ' +1722          'every render.',1723      );1724    }1725  }1726}1727function flushRenderPhaseStrictModeWarningsInDEV() {1728  if (__DEV__) {1729    ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings();1730    ReactStrictModeWarnings.flushLegacyContextWarning();1731    if (warnAboutDeprecatedLifecycles) {1732      ReactStrictModeWarnings.flushPendingDeprecationWarnings();1733    }1734  }1735}1736function stopFinishedWorkLoopTimer() {1737  const didCompleteRoot = true;1738  stopWorkLoopTimer(interruptedBy, didCompleteRoot);1739  interruptedBy = null;1740}1741function stopInterruptedWorkLoopTimer() {1742  // TODO: Track which fiber caused the interruption.1743  const didCompleteRoot = false;1744  stopWorkLoopTimer(interruptedBy, didCompleteRoot);1745  interruptedBy = null;1746}1747function checkForInterruption(1748  fiberThatReceivedUpdate: Fiber,1749  updateExpirationTime: ExpirationTime,1750) {1751  if (1752    enableUserTimingAPI &&1753    workInProgressRoot !== null &&1754    updateExpirationTime > renderExpirationTime1755  ) {1756    interruptedBy = fiberThatReceivedUpdate;1757  }1758}1759let didWarnStateUpdateForUnmountedComponent: Set<string> | null = null;1760function warnAboutUpdateOnUnmountedFiberInDEV(fiber) {1761  if (__DEV__) {...ReactFiberScheduler.new.js
Source:ReactFiberScheduler.new.js  
...287    warnAboutUpdateOnUnmountedFiberInDEV(fiber);288    return;289  }290  root.pingTime = NoWork;291  checkForInterruption(fiber, expirationTime);292  recordScheduleUpdate();293  if (expirationTime === Sync) {294    if (workPhase === LegacyUnbatchedPhase) {295      // This is a legacy edge case. The initial mount of a ReactDOM.render-ed296      // root inside of batchedUpdates should be synchronous, but layout updates297      // should be deferred until the end of the batch.298      let callback = renderRoot(root, Sync, true);299      while (callback !== null) {300        callback = callback(true);301      }302    } else {303      scheduleCallbackForRoot(root, ImmediatePriority, Sync);304      if (workPhase === NotWorking) {305        // Flush the synchronous work now, wnless we're already working or inside306        // a batch. This is intentionally inside scheduleUpdateOnFiber instead of307        // scheduleCallbackForFiber to preserve the ability to schedule a callback308        // without immediately flushing it. We only do this for user-initated309        // updates, to preserve historical behavior of sync mode.310        flushImmediateQueue();311      }312    }313  } else {314    // TODO: computeExpirationForFiber also reads the priority. Pass the315    // priority as an argument to that function and this one.316    const priorityLevel = getCurrentPriorityLevel();317    if (priorityLevel === UserBlockingPriority) {318      // This is the result of a discrete event. Track the lowest priority319      // discrete update per root so we can flush them early, if needed.320      if (rootsWithPendingDiscreteUpdates === null) {321        rootsWithPendingDiscreteUpdates = new Map([[root, expirationTime]]);322      } else {323        const lastDiscreteTime = rootsWithPendingDiscreteUpdates.get(root);324        if (325          lastDiscreteTime === undefined ||326          lastDiscreteTime > expirationTime327        ) {328          rootsWithPendingDiscreteUpdates.set(root, expirationTime);329        }330      }331    }332    scheduleCallbackForRoot(root, priorityLevel, expirationTime);333  }334}335export const scheduleWork = scheduleUpdateOnFiber;336// This is split into a separate function so we can mark a fiber with pending337// work without treating it as a typical update that originates from an event;338// e.g. retrying a Suspense boundary isn't an update, but it does schedule work339// on a fiber.340function markUpdateTimeFromFiberToRoot(fiber, expirationTime) {341  // Update the source fiber's expiration time342  if (fiber.expirationTime < expirationTime) {343    fiber.expirationTime = expirationTime;344  }345  let alternate = fiber.alternate;346  if (alternate !== null && alternate.expirationTime < expirationTime) {347    alternate.expirationTime = expirationTime;348  }349  // Walk the parent path to the root and update the child expiration time.350  let node = fiber.return;351  let root = null;352  if (node === null && fiber.tag === HostRoot) {353    root = fiber.stateNode;354  } else {355    while (node !== null) {356      alternate = node.alternate;357      if (node.childExpirationTime < expirationTime) {358        node.childExpirationTime = expirationTime;359        if (360          alternate !== null &&361          alternate.childExpirationTime < expirationTime362        ) {363          alternate.childExpirationTime = expirationTime;364        }365      } else if (366        alternate !== null &&367        alternate.childExpirationTime < expirationTime368      ) {369        alternate.childExpirationTime = expirationTime;370      }371      if (node.return === null && node.tag === HostRoot) {372        root = node.stateNode;373        break;374      }375      node = node.return;376    }377  }378  if (root !== null) {379    // Update the first and last pending expiration times in this root380    const firstPendingTime = root.firstPendingTime;381    if (expirationTime > firstPendingTime) {382      root.firstPendingTime = expirationTime;383    }384    const lastPendingTime = root.lastPendingTime;385    if (lastPendingTime === NoWork || expirationTime < lastPendingTime) {386      root.lastPendingTime = expirationTime;387    }388  }389  return root;390}391// Use this function, along with runRootCallback, to ensure that only a single392// callback per root is scheduled. It's still possible to call renderRoot393// directly, but scheduling via this function helps avoid excessive callbacks.394// It works by storing the callback node and expiration time on the root. When a395// new callback comes in, it compares the expiration time to determine if it396// should cancel the previous one. It also relies on commitRoot scheduling a397// callback to render the next level, because that means we don't need a398// separate callback per expiration time.399function scheduleCallbackForRoot(400  root: FiberRoot,401  priorityLevel: ReactPriorityLevel,402  expirationTime: ExpirationTime,403) {404  const existingCallbackExpirationTime = root.callbackExpirationTime;405  if (existingCallbackExpirationTime < expirationTime) {406    // New callback has higher priority than the existing one.407    const existingCallbackNode = root.callbackNode;408    if (existingCallbackNode !== null) {409      cancelCallback(existingCallbackNode);410    }411    root.callbackExpirationTime = expirationTime;412    const options =413      expirationTime === Sync414        ? null415        : {timeout: expirationTimeToMs(expirationTime)};416    root.callbackNode = scheduleCallback(417      priorityLevel,418      runRootCallback.bind(419        null,420        root,421        renderRoot.bind(null, root, expirationTime),422      ),423      options,424    );425    if (426      enableUserTimingAPI &&427      expirationTime !== Sync &&428      workPhase !== RenderPhase &&429      workPhase !== CommitPhase430    ) {431      // Scheduled an async callback, and we're not already working. Add an432      // entry to the flamegraph that shows we're waiting for a callback433      // to fire.434      startRequestCallbackTimer();435    }436  }437  const timeoutHandle = root.timeoutHandle;438  if (timeoutHandle !== noTimeout) {439    // The root previous suspended and scheduled a timeout to commit a fallback440    // state. Now that we have additional work, cancel the timeout.441    root.timeoutHandle = noTimeout;442    // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above443    cancelTimeout(timeoutHandle);444  }445  // Add the current set of interactions to the pending set associated with446  // this root.447  schedulePendingInteraction(root, expirationTime);448}449function runRootCallback(root, callback, isSync) {450  const prevCallbackNode = root.callbackNode;451  let continuation = null;452  try {453    continuation = callback(isSync);454    if (continuation !== null) {455      return runRootCallback.bind(null, root, continuation);456    } else {457      return null;458    }459  } finally {460    // If the callback exits without returning a continuation, remove the461    // corresponding callback node from the root. Unless the callback node462    // has changed, which implies that it was already cancelled by a high463    // priority update.464    if (continuation === null && prevCallbackNode === root.callbackNode) {465      root.callbackNode = null;466      root.callbackExpirationTime = NoWork;467    }468  }469}470export function flushRoot(root: FiberRoot, expirationTime: ExpirationTime) {471  if (workPhase === RenderPhase || workPhase === CommitPhase) {472    invariant(473      false,474      'work.commit(): Cannot commit while already rendering. This likely ' +475        'means you attempted to commit from inside a lifecycle method.',476    );477  }478  scheduleCallback(479    ImmediatePriority,480    renderRoot.bind(null, root, expirationTime),481  );482  flushImmediateQueue();483}484export function flushInteractiveUpdates() {485  if (workPhase === RenderPhase || workPhase === CommitPhase) {486    // Can't synchronously flush interactive updates if React is already487    // working. This is currently a no-op.488    // TODO: Should we fire a warning? This happens if you synchronously invoke489    // an input event inside an effect, like with `element.click()`.490    return;491  }492  flushPendingDiscreteUpdates();493}494function resolveLocksOnRoot(root: FiberRoot, expirationTime: ExpirationTime) {495  const firstBatch = root.firstBatch;496  if (497    firstBatch !== null &&498    firstBatch._defer &&499    firstBatch._expirationTime >= expirationTime500  ) {501    root.finishedWork = root.current.alternate;502    root.pendingCommitExpirationTime = expirationTime;503    scheduleCallback(NormalPriority, () => {504      firstBatch._onComplete();505      return null;506    });507    return true;508  } else {509    return false;510  }511}512export function deferredUpdates<A>(fn: () => A): A {513  // TODO: Remove in favor of Scheduler.next514  return runWithPriority(NormalPriority, fn);515}516export function interactiveUpdates<A, B, C, R>(517  fn: (A, B, C) => R,518  a: A,519  b: B,520  c: C,521): R {522  if (workPhase === NotWorking) {523    // TODO: Remove this call. Instead of doing this automatically, the caller524    // should explicitly call flushInteractiveUpdates.525    flushPendingDiscreteUpdates();526  }527  return runWithPriority(UserBlockingPriority, fn.bind(null, a, b, c));528}529export function syncUpdates<A, B, C, R>(530  fn: (A, B, C) => R,531  a: A,532  b: B,533  c: C,534): R {535  return runWithPriority(ImmediatePriority, fn.bind(null, a, b, c));536}537function flushPendingDiscreteUpdates() {538  if (rootsWithPendingDiscreteUpdates !== null) {539    // For each root with pending discrete updates, schedule a callback to540    // immediately flush them.541    const roots = rootsWithPendingDiscreteUpdates;542    rootsWithPendingDiscreteUpdates = null;543    roots.forEach((expirationTime, root) => {544      scheduleCallback(545        ImmediatePriority,546        renderRoot.bind(null, root, expirationTime),547      );548    });549    // Now flush the immediate queue.550    flushImmediateQueue();551  }552}553export function batchedUpdates<A, R>(fn: A => R, a: A): R {554  if (workPhase !== NotWorking) {555    // We're already working, or inside a batch, so batchedUpdates is a no-op.556    return fn(a);557  }558  workPhase = BatchedPhase;559  try {560    return fn(a);561  } finally {562    workPhase = NotWorking;563    // Flush the immediate callbacks that were scheduled during this batch564    flushImmediateQueue();565  }566}567export function unbatchedUpdates<A, R>(fn: (a: A) => R, a: A): R {568  if (workPhase !== BatchedPhase && workPhase !== FlushSyncPhase) {569    // We're not inside batchedUpdates or flushSync, so unbatchedUpdates is570    // a no-op.571    return fn(a);572  }573  const prevWorkPhase = workPhase;574  workPhase = LegacyUnbatchedPhase;575  try {576    return fn(a);577  } finally {578    workPhase = prevWorkPhase;579  }580}581export function flushSync<A, R>(fn: A => R, a: A): R {582  if (workPhase === RenderPhase || workPhase === CommitPhase) {583    invariant(584      false,585      'flushSync was called from inside a lifecycle method. It cannot be ' +586        'called when React is already rendering.',587    );588  }589  const prevWorkPhase = workPhase;590  workPhase = FlushSyncPhase;591  try {592    return runWithPriority(ImmediatePriority, fn.bind(null, a));593  } finally {594    workPhase = prevWorkPhase;595    // Flush the immediate callbacks that were scheduled during this batch.596    // Note that this will happen even if batchedUpdates is higher up597    // the stack.598    flushImmediateQueue();599  }600}601export function flushControlled(fn: () => mixed): void {602  const prevWorkPhase = workPhase;603  workPhase = BatchedPhase;604  try {605    runWithPriority(ImmediatePriority, fn);606  } finally {607    workPhase = prevWorkPhase;608    if (workPhase === NotWorking) {609      // Flush the immediate callbacks that were scheduled during this batch610      flushImmediateQueue();611    }612  }613}614function prepareFreshStack(root, expirationTime) {615  root.pendingCommitExpirationTime = NoWork;616  if (workInProgress !== null) {617    let interruptedWork = workInProgress.return;618    while (interruptedWork !== null) {619      unwindInterruptedWork(interruptedWork);620      interruptedWork = interruptedWork.return;621    }622  }623  workInProgressRoot = root;624  workInProgress = createWorkInProgress(root.current, null, expirationTime);625  renderExpirationTime = expirationTime;626  workInProgressRootExitStatus = RootIncomplete;627  workInProgressRootAbsoluteTimeoutMs = -1;628  if (__DEV__) {629    ReactStrictModeWarnings.discardPendingWarnings();630  }631}632function renderRoot(633  root: FiberRoot,634  expirationTime: ExpirationTime,635  isSync: boolean,636): SchedulerCallback | null {637  invariant(638    workPhase !== RenderPhase && workPhase !== CommitPhase,639    'Should not already be working.',640  );641  if (enableUserTimingAPI && expirationTime !== Sync) {642    const didExpire = isSync;643    const timeoutMs = expirationTimeToMs(expirationTime);644    stopRequestCallbackTimer(didExpire, timeoutMs);645  }646  if (root.firstPendingTime < expirationTime) {647    // If there's no work left at this expiration time, exit immediately. This648    // happens when multiple callbacks are scheduled for a single root, but an649    // earlier callback flushes the work of a later one.650    return null;651  }652  if (root.pendingCommitExpirationTime === expirationTime) {653    // There's already a pending commit at this expiration time.654    root.pendingCommitExpirationTime = NoWork;655    return commitRoot.bind(null, root, expirationTime);656  }657  flushPassiveEffects();658  // If the root or expiration time have changed, throw out the existing stack659  // and prepare a fresh one. Otherwise we'll continue where we left off.660  if (root !== workInProgressRoot || expirationTime !== renderExpirationTime) {661    prepareFreshStack(root, expirationTime);662    startWorkOnPendingInteraction(root, expirationTime);663  }664  // If we have a work-in-progress fiber, it means there's still work to do665  // in this root.666  if (workInProgress !== null) {667    const prevWorkPhase = workPhase;668    workPhase = RenderPhase;669    let prevDispatcher = ReactCurrentDispatcher.current;670    if (prevDispatcher === null) {671      // The React isomorphic package does not include a default dispatcher.672      // Instead the first renderer will lazily attach one, in order to give673      // nicer error messages.674      prevDispatcher = ContextOnlyDispatcher;675    }676    ReactCurrentDispatcher.current = ContextOnlyDispatcher;677    let prevInteractions: Set<Interaction> | null = null;678    if (enableSchedulerTracing) {679      prevInteractions = __interactionsRef.current;680      __interactionsRef.current = root.memoizedInteractions;681    }682    startWorkLoopTimer(workInProgress);683    // TODO: Fork renderRoot into renderRootSync and renderRootAsync684    if (isSync) {685      if (expirationTime !== Sync) {686        // An async update expired. There may be other expired updates on687        // this root. We should render all the expired work in a688        // single batch.689        const currentTime = requestCurrentTime();690        if (currentTime < expirationTime) {691          // Restart at the current time.692          workPhase = prevWorkPhase;693          resetContextDependencies();694          ReactCurrentDispatcher.current = prevDispatcher;695          if (enableSchedulerTracing) {696            __interactionsRef.current = ((prevInteractions: any): Set<697              Interaction,698            >);699          }700          return renderRoot.bind(null, root, currentTime);701        }702      }703    } else {704      // Since we know we're in a React event, we can clear the current705      // event time. The next update will compute a new event time.706      currentEventTime = NoWork;707    }708    do {709      try {710        if (isSync) {711          workLoopSync();712        } else {713          workLoop();714        }715        break;716      } catch (thrownValue) {717        // Reset module-level state that was set during the render phase.718        resetContextDependencies();719        resetHooks();720        const sourceFiber = workInProgress;721        if (sourceFiber === null || sourceFiber.return === null) {722          // Expected to be working on a non-root fiber. This is a fatal error723          // because there's no ancestor that can handle it; the root is724          // supposed to capture all errors that weren't caught by an error725          // boundary.726          prepareFreshStack(root, expirationTime);727          workPhase = prevWorkPhase;728          throw thrownValue;729        }730        if (enableProfilerTimer && sourceFiber.mode & ProfileMode) {731          // Record the time spent rendering before an error was thrown. This732          // avoids inaccurate Profiler durations in the case of a733          // suspended render.734          stopProfilerTimerIfRunningAndRecordDelta(sourceFiber, true);735        }736        const returnFiber = sourceFiber.return;737        throwException(738          root,739          returnFiber,740          sourceFiber,741          thrownValue,742          renderExpirationTime,743        );744        workInProgress = completeUnitOfWork(sourceFiber);745      }746    } while (true);747    workPhase = prevWorkPhase;748    resetContextDependencies();749    ReactCurrentDispatcher.current = prevDispatcher;750    if (enableSchedulerTracing) {751      __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);752    }753    if (workInProgress !== null) {754      // There's still work left over. Return a continuation.755      stopInterruptedWorkLoopTimer();756      if (expirationTime !== Sync) {757        startRequestCallbackTimer();758      }759      return renderRoot.bind(null, root, expirationTime);760    }761  }762  // We now have a consistent tree. The next step is either to commit it, or, if763  // something suspended, wait to commit it after a timeout.764  stopFinishedWorkLoopTimer();765  const isLocked = resolveLocksOnRoot(root, expirationTime);766  if (isLocked) {767    // This root has a lock that prevents it from committing. Exit. If we begin768    // work on the root again, without any intervening updates, it will finish769    // without doing additional work.770    return null;771  }772  // Set this to null to indicate there's no in-progress render.773  workInProgressRoot = null;774  switch (workInProgressRootExitStatus) {775    case RootIncomplete: {776      invariant(false, 'Should have a work-in-progress.');777    }778    // Flow knows about invariant, so it compains if I add a break statement,779    // but eslint doesn't know about invariant, so it complains if I do.780    // eslint-disable-next-line no-fallthrough781    case RootErrored: {782      // An error was thrown. First check if there is lower priority work783      // scheduled on this root.784      const lastPendingTime = root.lastPendingTime;785      if (root.lastPendingTime < expirationTime) {786        // There's lower priority work. Before raising the error, try rendering787        // at the lower priority to see if it fixes it. Use a continuation to788        // maintain the existing priority and position in the queue.789        return renderRoot.bind(null, root, lastPendingTime);790      }791      if (!isSync) {792        // If we're rendering asynchronously, it's possible the error was793        // caused by tearing due to a mutation during an event. Try rendering794        // one more time without yiedling to events.795        prepareFreshStack(root, expirationTime);796        scheduleCallback(797          ImmediatePriority,798          renderRoot.bind(null, root, expirationTime),799        );800        return null;801      }802      // If we're already rendering synchronously, commit the root in its803      // errored state.804      return commitRoot.bind(null, root, expirationTime);805    }806    case RootSuspended: {807      const lastPendingTime = root.lastPendingTime;808      if (root.lastPendingTime < expirationTime) {809        // There's lower priority work. It might be unsuspended. Try rendering810        // at that level.811        return renderRoot.bind(null, root, lastPendingTime);812      }813      if (!isSync) {814        const msUntilTimeout = computeMsUntilTimeout(815          root,816          workInProgressRootAbsoluteTimeoutMs,817        );818        if (msUntilTimeout > 0) {819          // The render is suspended, it hasn't timed out, and there's no lower820          // priority work to do. Instead of committing the fallback821          // immediately, wait for more data to arrive.822          root.timeoutHandle = scheduleTimeout(823            commitRoot.bind(null, root, expirationTime),824            msUntilTimeout,825          );826          return null;827        }828      }829      // The work expired. Commit immediately.830      return commitRoot.bind(null, root, expirationTime);831    }832    case RootCompleted: {833      // The work completed. Ready to commit.834      return commitRoot.bind(null, root, expirationTime);835    }836    default: {837      invariant(false, 'Unknown root exit status.');838    }839  }840}841export function renderDidSuspend(842  root: FiberRoot,843  absoluteTimeoutMs: number,844  // TODO: Don't need this argument anymore845  suspendedTime: ExpirationTime,846) {847  if (848    absoluteTimeoutMs >= 0 &&849    workInProgressRootAbsoluteTimeoutMs < absoluteTimeoutMs850  ) {851    workInProgressRootAbsoluteTimeoutMs = absoluteTimeoutMs;852    if (workInProgressRootExitStatus === RootIncomplete) {853      workInProgressRootExitStatus = RootSuspended;854    }855  }856}857export function renderDidError() {858  if (859    workInProgressRootExitStatus === RootIncomplete ||860    workInProgressRootExitStatus === RootSuspended861  ) {862    workInProgressRootExitStatus = RootErrored;863  }864}865function workLoopSync() {866  // Already timed out, so perform work without checking if we need to yield.867  while (workInProgress !== null) {868    workInProgress = performUnitOfWork(workInProgress);869  }870}871function workLoop() {872  // Perform work until Scheduler asks us to yield873  while (workInProgress !== null && !shouldYield()) {874    workInProgress = performUnitOfWork(workInProgress);875  }876}877function performUnitOfWork(unitOfWork: Fiber): Fiber | null {878  // The current, flushed, state of this fiber is the alternate. Ideally879  // nothing should rely on this, but relying on it here means that we don't880  // need an additional field on the work in progress.881  const current = unitOfWork.alternate;882  startWorkTimer(unitOfWork);883  setCurrentDebugFiberInDEV(unitOfWork);884  let next;885  if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoContext) {886    startProfilerTimer(unitOfWork);887    next = beginWork(current, unitOfWork, renderExpirationTime);888    stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);889  } else {890    next = beginWork(current, unitOfWork, renderExpirationTime);891  }892  resetCurrentDebugFiberInDEV();893  unitOfWork.memoizedProps = unitOfWork.pendingProps;894  if (next === null) {895    // If this doesn't spawn new work, complete the current work.896    next = completeUnitOfWork(unitOfWork);897  }898  ReactCurrentOwner.current = null;899  return next;900}901function completeUnitOfWork(unitOfWork: Fiber): Fiber | null {902  // Attempt to complete the current unit of work, then move to the next903  // sibling. If there are no more siblings, return to the parent fiber.904  workInProgress = unitOfWork;905  do {906    // The current, flushed, state of this fiber is the alternate. Ideally907    // nothing should rely on this, but relying on it here means that we don't908    // need an additional field on the work in progress.909    const current = workInProgress.alternate;910    const returnFiber = workInProgress.return;911    // Check if the work completed or if something threw.912    if ((workInProgress.effectTag & Incomplete) === NoEffect) {913      setCurrentDebugFiberInDEV(workInProgress);914      let next;915      if (916        !enableProfilerTimer ||917        (workInProgress.mode & ProfileMode) === NoContext918      ) {919        next = completeWork(current, workInProgress, renderExpirationTime);920      } else {921        startProfilerTimer(workInProgress);922        next = completeWork(current, workInProgress, renderExpirationTime);923        // Update render duration assuming we didn't error.924        stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);925      }926      stopWorkTimer(workInProgress);927      resetCurrentDebugFiberInDEV();928      resetChildExpirationTime(workInProgress);929      if (next !== null) {930        // Completing this fiber spawned new work. Work on that next.931        return next;932      }933      if (934        returnFiber !== null &&935        // Do not append effects to parents if a sibling failed to complete936        (returnFiber.effectTag & Incomplete) === NoEffect937      ) {938        // Append all the effects of the subtree and this fiber onto the effect939        // list of the parent. The completion order of the children affects the940        // side-effect order.941        if (returnFiber.firstEffect === null) {942          returnFiber.firstEffect = workInProgress.firstEffect;943        }944        if (workInProgress.lastEffect !== null) {945          if (returnFiber.lastEffect !== null) {946            returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;947          }948          returnFiber.lastEffect = workInProgress.lastEffect;949        }950        // If this fiber had side-effects, we append it AFTER the children's951        // side-effects. We can perform certain side-effects earlier if needed,952        // by doing multiple passes over the effect list. We don't want to953        // schedule our own side-effect on our own list because if end up954        // reusing children we'll schedule this effect onto itself since we're955        // at the end.956        const effectTag = workInProgress.effectTag;957        // Skip both NoWork and PerformedWork tags when creating the effect958        // list. PerformedWork effect is read by React DevTools but shouldn't be959        // committed.960        if (effectTag > PerformedWork) {961          if (returnFiber.lastEffect !== null) {962            returnFiber.lastEffect.nextEffect = workInProgress;963          } else {964            returnFiber.firstEffect = workInProgress;965          }966          returnFiber.lastEffect = workInProgress;967        }968      }969    } else {970      // This fiber did not complete because something threw. Pop values off971      // the stack without entering the complete phase. If this is a boundary,972      // capture values if possible.973      const next = unwindWork(workInProgress, renderExpirationTime);974      // Because this fiber did not complete, don't reset its expiration time.975      if (976        enableProfilerTimer &&977        (workInProgress.mode & ProfileMode) !== NoContext978      ) {979        // Record the render duration for the fiber that errored.980        stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);981        // Include the time spent working on failed children before continuing.982        let actualDuration = workInProgress.actualDuration;983        let child = workInProgress.child;984        while (child !== null) {985          actualDuration += child.actualDuration;986          child = child.sibling;987        }988        workInProgress.actualDuration = actualDuration;989      }990      if (next !== null) {991        // If completing this work spawned new work, do that next. We'll come992        // back here again.993        // Since we're restarting, remove anything that is not a host effect994        // from the effect tag.995        // TODO: The name stopFailedWorkTimer is misleading because Suspense996        // also captures and restarts.997        stopFailedWorkTimer(workInProgress);998        next.effectTag &= HostEffectMask;999        return next;1000      }1001      stopWorkTimer(workInProgress);1002      if (returnFiber !== null) {1003        // Mark the parent fiber as incomplete and clear its effect list.1004        returnFiber.firstEffect = returnFiber.lastEffect = null;1005        returnFiber.effectTag |= Incomplete;1006      }1007    }1008    const siblingFiber = workInProgress.sibling;1009    if (siblingFiber !== null) {1010      // If there is more work to do in this returnFiber, do that next.1011      return siblingFiber;1012    }1013    // Otherwise, return to the parent1014    workInProgress = returnFiber;1015  } while (workInProgress !== null);1016  // We've reached the root.1017  if (workInProgressRootExitStatus === RootIncomplete) {1018    workInProgressRootExitStatus = RootCompleted;1019  }1020  return null;1021}1022function resetChildExpirationTime(completedWork: Fiber) {1023  if (1024    renderExpirationTime !== Never &&1025    completedWork.childExpirationTime === Never1026  ) {1027    // The children of this component are hidden. Don't bubble their1028    // expiration times.1029    return;1030  }1031  let newChildExpirationTime = NoWork;1032  // Bubble up the earliest expiration time.1033  if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoContext) {1034    // In profiling mode, resetChildExpirationTime is also used to reset1035    // profiler durations.1036    let actualDuration = completedWork.actualDuration;1037    let treeBaseDuration = completedWork.selfBaseDuration;1038    // When a fiber is cloned, its actualDuration is reset to 0. This value will1039    // only be updated if work is done on the fiber (i.e. it doesn't bailout).1040    // When work is done, it should bubble to the parent's actualDuration. If1041    // the fiber has not been cloned though, (meaning no work was done), then1042    // this value will reflect the amount of time spent working on a previous1043    // render. In that case it should not bubble. We determine whether it was1044    // cloned by comparing the child pointer.1045    const shouldBubbleActualDurations =1046      completedWork.alternate === null ||1047      completedWork.child !== completedWork.alternate.child;1048    let child = completedWork.child;1049    while (child !== null) {1050      const childUpdateExpirationTime = child.expirationTime;1051      const childChildExpirationTime = child.childExpirationTime;1052      if (childUpdateExpirationTime > newChildExpirationTime) {1053        newChildExpirationTime = childUpdateExpirationTime;1054      }1055      if (childChildExpirationTime > newChildExpirationTime) {1056        newChildExpirationTime = childChildExpirationTime;1057      }1058      if (shouldBubbleActualDurations) {1059        actualDuration += child.actualDuration;1060      }1061      treeBaseDuration += child.treeBaseDuration;1062      child = child.sibling;1063    }1064    completedWork.actualDuration = actualDuration;1065    completedWork.treeBaseDuration = treeBaseDuration;1066  } else {1067    let child = completedWork.child;1068    while (child !== null) {1069      const childUpdateExpirationTime = child.expirationTime;1070      const childChildExpirationTime = child.childExpirationTime;1071      if (childUpdateExpirationTime > newChildExpirationTime) {1072        newChildExpirationTime = childUpdateExpirationTime;1073      }1074      if (childChildExpirationTime > newChildExpirationTime) {1075        newChildExpirationTime = childChildExpirationTime;1076      }1077      child = child.sibling;1078    }1079  }1080  completedWork.childExpirationTime = newChildExpirationTime;1081}1082function commitRoot(root, expirationTime) {1083  runWithPriority(1084    ImmediatePriority,1085    commitRootImpl.bind(null, root, expirationTime),1086  );1087  // If there are passive effects, schedule a callback to flush them. This goes1088  // outside commitRootImpl so that it inherits the priority of the render.1089  if (rootWithPendingPassiveEffects !== null) {1090    const priorityLevel = getCurrentPriorityLevel();1091    scheduleCallback(priorityLevel, () => {1092      flushPassiveEffects();1093      return null;1094    });1095  }1096  return null;1097}1098function commitRootImpl(root, expirationTime) {1099  flushPassiveEffects();1100  flushRenderPhaseStrictModeWarningsInDEV();1101  invariant(1102    workPhase !== RenderPhase && workPhase !== CommitPhase,1103    'Should not already be working.',1104  );1105  const finishedWork = root.current.alternate;1106  invariant(finishedWork !== null, 'Should have a work-in-progress root.');1107  // commitRoot never returns a continuation; it always finishes synchronously.1108  // So we can clear these now to allow a new callback to be scheduled.1109  root.callbackNode = null;1110  root.callbackExpirationTime = NoWork;1111  startCommitTimer();1112  // Update the first and last pending times on this root. The new first1113  // pending time is whatever is left on the root fiber.1114  const updateExpirationTimeBeforeCommit = finishedWork.expirationTime;1115  const childExpirationTimeBeforeCommit = finishedWork.childExpirationTime;1116  const firstPendingTimeBeforeCommit =1117    childExpirationTimeBeforeCommit > updateExpirationTimeBeforeCommit1118      ? childExpirationTimeBeforeCommit1119      : updateExpirationTimeBeforeCommit;1120  root.firstPendingTime = firstPendingTimeBeforeCommit;1121  if (firstPendingTimeBeforeCommit < root.lastPendingTime) {1122    // This usually means we've finished all the work, but it can also happen1123    // when something gets downprioritized during render, like a hidden tree.1124    root.lastPendingTime = firstPendingTimeBeforeCommit;1125  }1126  if (root === workInProgressRoot) {1127    // We can reset these now that they are finished.1128    workInProgressRoot = null;1129    workInProgress = null;1130    renderExpirationTime = NoWork;1131  } else {1132    // This indicates that the last root we worked on is not the same one that1133    // we're committing now. This most commonly happens when a suspended root1134    // times out.1135  }1136  // Get the list of effects.1137  let firstEffect;1138  if (finishedWork.effectTag > PerformedWork) {1139    // A fiber's effect list consists only of its children, not itself. So if1140    // the root has an effect, we need to add it to the end of the list. The1141    // resulting list is the set that would belong to the root's parent, if it1142    // had one; that is, all the effects in the tree including the root.1143    if (finishedWork.lastEffect !== null) {1144      finishedWork.lastEffect.nextEffect = finishedWork;1145      firstEffect = finishedWork.firstEffect;1146    } else {1147      firstEffect = finishedWork;1148    }1149  } else {1150    // There is no effect on the root.1151    firstEffect = finishedWork.firstEffect;1152  }1153  if (firstEffect !== null) {1154    const prevWorkPhase = workPhase;1155    workPhase = CommitPhase;1156    let prevInteractions: Set<Interaction> | null = null;1157    if (enableSchedulerTracing) {1158      prevInteractions = __interactionsRef.current;1159      __interactionsRef.current = root.memoizedInteractions;1160    }1161    // Reset this to null before calling lifecycles1162    ReactCurrentOwner.current = null;1163    // The commit phase is broken into several sub-phases. We do a separate pass1164    // of the effect list for each phase: all mutation effects come before all1165    // layout effects, and so on.1166    // The first phase a "before mutation" phase. We use this phase to read the1167    // state of the host tree right before we mutate it. This is where1168    // getSnapshotBeforeUpdate is called.1169    startCommitSnapshotEffectsTimer();1170    prepareForCommit(root.containerInfo);1171    nextEffect = firstEffect;1172    do {1173      if (__DEV__) {1174        invokeGuardedCallback(null, commitBeforeMutationEffects, null);1175        if (hasCaughtError()) {1176          invariant(nextEffect !== null, 'Should be working on an effect.');1177          const error = clearCaughtError();1178          captureCommitPhaseError(nextEffect, error);1179          nextEffect = nextEffect.nextEffect;1180        }1181      } else {1182        try {1183          commitBeforeMutationEffects();1184        } catch (error) {1185          invariant(nextEffect !== null, 'Should be working on an effect.');1186          captureCommitPhaseError(nextEffect, error);1187          nextEffect = nextEffect.nextEffect;1188        }1189      }1190    } while (nextEffect !== null);1191    stopCommitSnapshotEffectsTimer();1192    if (enableProfilerTimer) {1193      // Mark the current commit time to be shared by all Profilers in this1194      // batch. This enables them to be grouped later.1195      recordCommitTime();1196    }1197    // The next phase is the mutation phase, where we mutate the host tree.1198    startCommitHostEffectsTimer();1199    nextEffect = firstEffect;1200    do {1201      if (__DEV__) {1202        invokeGuardedCallback(null, commitMutationEffects, null);1203        if (hasCaughtError()) {1204          invariant(nextEffect !== null, 'Should be working on an effect.');1205          const error = clearCaughtError();1206          captureCommitPhaseError(nextEffect, error);1207          nextEffect = nextEffect.nextEffect;1208        }1209      } else {1210        try {1211          commitMutationEffects();1212        } catch (error) {1213          invariant(nextEffect !== null, 'Should be working on an effect.');1214          captureCommitPhaseError(nextEffect, error);1215          nextEffect = nextEffect.nextEffect;1216        }1217      }1218    } while (nextEffect !== null);1219    stopCommitHostEffectsTimer();1220    resetAfterCommit(root.containerInfo);1221    // The work-in-progress tree is now the current tree. This must come after1222    // the mutation phase, so that the previous tree is still current during1223    // componentWillUnmount, but before the layout phase, so that the finished1224    // work is current during componentDidMount/Update.1225    root.current = finishedWork;1226    // The next phase is the layout phase, where we call effects that read1227    // the host tree after it's been mutated. The idiomatic use case for this is1228    // layout, but class component lifecycles also fire here for legacy reasons.1229    startCommitLifeCyclesTimer();1230    nextEffect = firstEffect;1231    do {1232      if (__DEV__) {1233        invokeGuardedCallback(1234          null,1235          commitLayoutEffects,1236          null,1237          root,1238          expirationTime,1239        );1240        if (hasCaughtError()) {1241          invariant(nextEffect !== null, 'Should be working on an effect.');1242          const error = clearCaughtError();1243          captureCommitPhaseError(nextEffect, error);1244          nextEffect = nextEffect.nextEffect;1245        }1246      } else {1247        try {1248          commitLayoutEffects(root, expirationTime);1249        } catch (error) {1250          invariant(nextEffect !== null, 'Should be working on an effect.');1251          captureCommitPhaseError(nextEffect, error);1252          nextEffect = nextEffect.nextEffect;1253        }1254      }1255    } while (nextEffect !== null);1256    stopCommitLifeCyclesTimer();1257    nextEffect = null;1258    if (enableSchedulerTracing) {1259      __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);1260    }1261    workPhase = prevWorkPhase;1262  } else {1263    // No effects.1264    root.current = finishedWork;1265    // Measure these anyway so the flamegraph explicitly shows that there were1266    // no effects.1267    // TODO: Maybe there's a better way to report this.1268    startCommitSnapshotEffectsTimer();1269    stopCommitSnapshotEffectsTimer();1270    if (enableProfilerTimer) {1271      recordCommitTime();1272    }1273    startCommitHostEffectsTimer();1274    stopCommitHostEffectsTimer();1275    startCommitLifeCyclesTimer();1276    stopCommitLifeCyclesTimer();1277  }1278  stopCommitTimer();1279  if (rootDoesHavePassiveEffects) {1280    // This commit has passive effects. Stash a reference to them. But don't1281    // schedule a callback until after flushing layout work.1282    rootDoesHavePassiveEffects = false;1283    rootWithPendingPassiveEffects = root;1284    pendingPassiveEffectsExpirationTime = expirationTime;1285  } else {1286    if (enableSchedulerTracing) {1287      // If there are no passive effects, then we can complete the pending1288      // interactions. Otherwise, we'll wait until after the passive effects1289      // are flushed.1290      finishPendingInteractions(root, expirationTime);1291    }1292  }1293  // Check if there's remaining work on this root1294  const remainingExpirationTime = root.firstPendingTime;1295  if (remainingExpirationTime !== NoWork) {1296    const currentTime = requestCurrentTime();1297    const priorityLevel = inferPriorityFromExpirationTime(1298      currentTime,1299      remainingExpirationTime,1300    );1301    scheduleCallbackForRoot(root, priorityLevel, remainingExpirationTime);1302  } else {1303    // If there's no remaining work, we can clear the set of already failed1304    // error boundaries.1305    legacyErrorBoundariesThatAlreadyFailed = null;1306  }1307  onCommitRoot(finishedWork.stateNode);1308  if (remainingExpirationTime === Sync) {1309    // Count the number of times the root synchronously re-renders without1310    // finishing. If there are too many, it indicates an infinite update loop.1311    if (root === rootWithNestedUpdates) {1312      nestedUpdateCount++;1313    } else {1314      nestedUpdateCount = 0;1315      rootWithNestedUpdates = root;1316    }1317  } else {1318    nestedUpdateCount = 0;1319  }1320  if (hasUncaughtError) {1321    hasUncaughtError = false;1322    const error = firstUncaughtError;1323    firstUncaughtError = null;1324    throw error;1325  }1326  if (workPhase === LegacyUnbatchedPhase) {1327    // This is a legacy edge case. We just committed the initial mount of1328    // a ReactDOM.render-ed root inside of batchedUpdates. The commit fired1329    // synchronously, but layout updates should be deferred until the end1330    // of the batch.1331    return null;1332  }1333  // If layout work was scheduled, flush it now.1334  flushImmediateQueue();1335  return null;1336}1337function commitBeforeMutationEffects() {1338  while (nextEffect !== null) {1339    if ((nextEffect.effectTag & Snapshot) !== NoEffect) {1340      setCurrentDebugFiberInDEV(nextEffect);1341      recordEffect();1342      const current = nextEffect.alternate;1343      commitBeforeMutationEffectOnFiber(current, nextEffect);1344      resetCurrentDebugFiberInDEV();1345    }1346    nextEffect = nextEffect.nextEffect;1347  }1348}1349function commitMutationEffects() {1350  // TODO: Should probably move the bulk of this function to commitWork.1351  while (nextEffect !== null) {1352    setCurrentDebugFiberInDEV(nextEffect);1353    const effectTag = nextEffect.effectTag;1354    if (effectTag & ContentReset) {1355      commitResetTextContent(nextEffect);1356    }1357    if (effectTag & Ref) {1358      const current = nextEffect.alternate;1359      if (current !== null) {1360        commitDetachRef(current);1361      }1362    }1363    // The following switch statement is only concerned about placement,1364    // updates, and deletions. To avoid needing to add a case for every possible1365    // bitmap value, we remove the secondary effects from the effect tag and1366    // switch on that value.1367    let primaryEffectTag = effectTag & (Placement | Update | Deletion);1368    switch (primaryEffectTag) {1369      case Placement: {1370        commitPlacement(nextEffect);1371        // Clear the "placement" from effect tag so that we know that this is1372        // inserted, before any life-cycles like componentDidMount gets called.1373        // TODO: findDOMNode doesn't rely on this any more but isMounted does1374        // and isMounted is deprecated anyway so we should be able to kill this.1375        nextEffect.effectTag &= ~Placement;1376        break;1377      }1378      case PlacementAndUpdate: {1379        // Placement1380        commitPlacement(nextEffect);1381        // Clear the "placement" from effect tag so that we know that this is1382        // inserted, before any life-cycles like componentDidMount gets called.1383        nextEffect.effectTag &= ~Placement;1384        // Update1385        const current = nextEffect.alternate;1386        commitWork(current, nextEffect);1387        break;1388      }1389      case Update: {1390        const current = nextEffect.alternate;1391        commitWork(current, nextEffect);1392        break;1393      }1394      case Deletion: {1395        commitDeletion(nextEffect);1396        break;1397      }1398    }1399    // TODO: Only record a mutation effect if primaryEffectTag is non-zero.1400    recordEffect();1401    resetCurrentDebugFiberInDEV();1402    nextEffect = nextEffect.nextEffect;1403  }1404}1405function commitLayoutEffects(1406  root: FiberRoot,1407  committedExpirationTime: ExpirationTime,1408) {1409  // TODO: Should probably move the bulk of this function to commitWork.1410  while (nextEffect !== null) {1411    setCurrentDebugFiberInDEV(nextEffect);1412    const effectTag = nextEffect.effectTag;1413    if (effectTag & (Update | Callback)) {1414      recordEffect();1415      const current = nextEffect.alternate;1416      commitLayoutEffectOnFiber(1417        root,1418        current,1419        nextEffect,1420        committedExpirationTime,1421      );1422    }1423    if (effectTag & Ref) {1424      recordEffect();1425      commitAttachRef(nextEffect);1426    }1427    if (effectTag & Passive) {1428      rootDoesHavePassiveEffects = true;1429    }1430    resetCurrentDebugFiberInDEV();1431    nextEffect = nextEffect.nextEffect;1432  }1433}1434export function flushPassiveEffects() {1435  if (rootWithPendingPassiveEffects === null) {1436    return false;1437  }1438  const root = rootWithPendingPassiveEffects;1439  const expirationTime = pendingPassiveEffectsExpirationTime;1440  rootWithPendingPassiveEffects = null;1441  pendingPassiveEffectsExpirationTime = NoWork;1442  let prevInteractions: Set<Interaction> | null = null;1443  if (enableSchedulerTracing) {1444    prevInteractions = __interactionsRef.current;1445    __interactionsRef.current = root.memoizedInteractions;1446  }1447  invariant(1448    workPhase !== RenderPhase && workPhase !== CommitPhase,1449    'Cannot flush passive effects while already rendering.',1450  );1451  const prevWorkPhase = workPhase;1452  workPhase = CommitPhase;1453  // Note: This currently assumes there are no passive effects on the root1454  // fiber, because the root is not part of its own effect list. This could1455  // change in the future.1456  let effect = root.current.firstEffect;1457  while (effect !== null) {1458    if (__DEV__) {1459      setCurrentDebugFiberInDEV(effect);1460      invokeGuardedCallback(null, commitPassiveHookEffects, null, effect);1461      if (hasCaughtError()) {1462        invariant(effect !== null, 'Should be working on an effect.');1463        const error = clearCaughtError();1464        captureCommitPhaseError(effect, error);1465      }1466      resetCurrentDebugFiberInDEV();1467    } else {1468      try {1469        commitPassiveHookEffects(effect);1470      } catch (error) {1471        invariant(effect !== null, 'Should be working on an effect.');1472        captureCommitPhaseError(effect, error);1473      }1474    }1475    effect = effect.nextEffect;1476  }1477  if (enableSchedulerTracing) {1478    __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);1479    finishPendingInteractions(root, expirationTime);1480  }1481  workPhase = prevWorkPhase;1482  flushImmediateQueue();1483  // If additional passive effects were scheduled, increment a counter. If this1484  // exceeds the limit, we'll fire a warning.1485  nestedPassiveUpdateCount =1486    rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;1487  return true;1488}1489export function isAlreadyFailedLegacyErrorBoundary(instance: mixed): boolean {1490  return (1491    legacyErrorBoundariesThatAlreadyFailed !== null &&1492    legacyErrorBoundariesThatAlreadyFailed.has(instance)1493  );1494}1495export function markLegacyErrorBoundaryAsFailed(instance: mixed) {1496  if (legacyErrorBoundariesThatAlreadyFailed === null) {1497    legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);1498  } else {1499    legacyErrorBoundariesThatAlreadyFailed.add(instance);1500  }1501}1502function prepareToThrowUncaughtError(error: mixed) {1503  if (!hasUncaughtError) {1504    hasUncaughtError = true;1505    firstUncaughtError = error;1506  }1507}1508export const onUncaughtError = prepareToThrowUncaughtError;1509function captureCommitPhaseErrorOnRoot(1510  rootFiber: Fiber,1511  sourceFiber: Fiber,1512  error: mixed,1513) {1514  const errorInfo = createCapturedValue(error, sourceFiber);1515  const update = createRootErrorUpdate(rootFiber, errorInfo, Sync);1516  enqueueUpdate(rootFiber, update);1517  const root = markUpdateTimeFromFiberToRoot(rootFiber, Sync);1518  if (root !== null) {1519    scheduleCallbackForRoot(root, ImmediatePriority, Sync);1520  }1521}1522export function captureCommitPhaseError(sourceFiber: Fiber, error: mixed) {1523  if (sourceFiber.tag === HostRoot) {1524    // Error was thrown at the root. There is no parent, so the root1525    // itself should capture it.1526    captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);1527    return;1528  }1529  let fiber = sourceFiber.return;1530  while (fiber !== null) {1531    if (fiber.tag === HostRoot) {1532      captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);1533      return;1534    } else if (fiber.tag === ClassComponent) {1535      const ctor = fiber.type;1536      const instance = fiber.stateNode;1537      if (1538        typeof ctor.getDerivedStateFromError === 'function' ||1539        (typeof instance.componentDidCatch === 'function' &&1540          !isAlreadyFailedLegacyErrorBoundary(instance))1541      ) {1542        const errorInfo = createCapturedValue(error, sourceFiber);1543        const update = createClassErrorUpdate(1544          fiber,1545          errorInfo,1546          // TODO: This is always sync1547          Sync,1548        );1549        enqueueUpdate(fiber, update);1550        const root = markUpdateTimeFromFiberToRoot(fiber, Sync);1551        if (root !== null) {1552          scheduleCallbackForRoot(root, ImmediatePriority, Sync);1553        }1554        return;1555      }1556    }1557    fiber = fiber.return;1558  }1559}1560export function pingSuspendedRoot(1561  root: FiberRoot,1562  thenable: Thenable,1563  suspendedTime: ExpirationTime,1564) {1565  const pingCache = root.pingCache;1566  if (pingCache !== null) {1567    // The thenable resolved, so we no longer need to memoize, because it will1568    // never be thrown again.1569    pingCache.delete(thenable);1570  }1571  if (workInProgressRoot === root && renderExpirationTime === suspendedTime) {1572    // Received a ping at the same priority level at which we're currently1573    // rendering. Restart from the root. Don't need to schedule a ping because1574    // we're already working on this tree.1575    prepareFreshStack(root, renderExpirationTime);1576    return;1577  }1578  const lastPendingTime = root.lastPendingTime;1579  if (lastPendingTime < suspendedTime) {1580    // The root is no longer suspended at this time.1581    return;1582  }1583  const pingTime = root.pingTime;1584  if (pingTime !== NoWork && pingTime < suspendedTime) {1585    // There's already a lower priority ping scheduled.1586    return;1587  }1588  // Mark the time at which this ping was scheduled.1589  root.pingTime = suspendedTime;1590  const currentTime = requestCurrentTime();1591  const priorityLevel = inferPriorityFromExpirationTime(1592    currentTime,1593    suspendedTime,1594  );1595  scheduleCallbackForRoot(root, priorityLevel, suspendedTime);1596}1597export function retryTimedOutBoundary(boundaryFiber: Fiber) {1598  // The boundary fiber (a Suspense component) previously timed out and was1599  // rendered in its fallback state. One of the promises that suspended it has1600  // resolved, which means at least part of the tree was likely unblocked. Try1601  // rendering again, at a new expiration time.1602  const currentTime = requestCurrentTime();1603  const retryTime = computeExpirationForFiber(currentTime, boundaryFiber);1604  // TODO: Special case idle priority?1605  const priorityLevel = inferPriorityFromExpirationTime(currentTime, retryTime);1606  const root = markUpdateTimeFromFiberToRoot(boundaryFiber, retryTime);1607  if (root !== null) {1608    scheduleCallbackForRoot(root, priorityLevel, retryTime);1609  }1610}1611export function resolveRetryThenable(boundaryFiber: Fiber, thenable: Thenable) {1612  let retryCache: WeakSet<Thenable> | Set<Thenable> | null;1613  if (enableSuspenseServerRenderer) {1614    switch (boundaryFiber.tag) {1615      case SuspenseComponent:1616        retryCache = boundaryFiber.stateNode;1617        break;1618      case DehydratedSuspenseComponent:1619        retryCache = boundaryFiber.memoizedState;1620        break;1621      default:1622        invariant(1623          false,1624          'Pinged unknown suspense boundary type. ' +1625            'This is probably a bug in React.',1626        );1627    }1628  } else {1629    retryCache = boundaryFiber.stateNode;1630  }1631  if (retryCache !== null) {1632    // The thenable resolved, so we no longer need to memoize, because it will1633    // never be thrown again.1634    retryCache.delete(thenable);1635  }1636  retryTimedOutBoundary(boundaryFiber);1637}1638export function inferStartTimeFromExpirationTime(1639  root: FiberRoot,1640  expirationTime: ExpirationTime,1641) {1642  // We don't know exactly when the update was scheduled, but we can infer an1643  // approximate start time from the expiration time.1644  const earliestExpirationTimeMs = expirationTimeToMs(root.firstPendingTime);1645  // TODO: Track this on the root instead. It's more accurate, doesn't rely on1646  // assumptions about priority, and isn't coupled to Scheduler details.1647  return earliestExpirationTimeMs - LOW_PRIORITY_EXPIRATION;1648}1649function computeMsUntilTimeout(root, absoluteTimeoutMs) {1650  if (disableYielding) {1651    // Timeout immediately when yielding is disabled.1652    return 0;1653  }1654  // Find the earliest uncommitted expiration time in the tree, including1655  // work that is suspended. The timeout threshold cannot be longer than1656  // the overall expiration.1657  const earliestExpirationTimeMs = expirationTimeToMs(root.firstPendingTime);1658  if (earliestExpirationTimeMs < absoluteTimeoutMs) {1659    absoluteTimeoutMs = earliestExpirationTimeMs;1660  }1661  // Subtract the current time from the absolute timeout to get the number1662  // of milliseconds until the timeout. In other words, convert an absolute1663  // timestamp to a relative time. This is the value that is passed1664  // to `setTimeout`.1665  let msUntilTimeout = absoluteTimeoutMs - now();1666  return msUntilTimeout < 0 ? 0 : msUntilTimeout;1667}1668function checkForNestedUpdates() {1669  if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {1670    nestedUpdateCount = 0;1671    rootWithNestedUpdates = null;1672    invariant(1673      false,1674      'Maximum update depth exceeded. This can happen when a component ' +1675        'repeatedly calls setState inside componentWillUpdate or ' +1676        'componentDidUpdate. React limits the number of nested updates to ' +1677        'prevent infinite loops.',1678    );1679  }1680  if (__DEV__) {1681    if (nestedPassiveUpdateCount > NESTED_PASSIVE_UPDATE_LIMIT) {1682      nestedPassiveUpdateCount = 0;1683      warning(1684        false,1685        'Maximum update depth exceeded. This can happen when a component ' +1686          "calls setState inside useEffect, but useEffect either doesn't " +1687          'have a dependency array, or one of the dependencies changes on ' +1688          'every render.',1689      );1690    }1691  }1692}1693function flushRenderPhaseStrictModeWarningsInDEV() {1694  if (__DEV__) {1695    ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings();1696    ReactStrictModeWarnings.flushLegacyContextWarning();1697    if (warnAboutDeprecatedLifecycles) {1698      ReactStrictModeWarnings.flushPendingDeprecationWarnings();1699    }1700  }1701}1702function stopFinishedWorkLoopTimer() {1703  const didCompleteRoot = true;1704  stopWorkLoopTimer(interruptedBy, didCompleteRoot);1705  interruptedBy = null;1706}1707function stopInterruptedWorkLoopTimer() {1708  // TODO: Track which fiber caused the interruption.1709  const didCompleteRoot = false;1710  stopWorkLoopTimer(interruptedBy, didCompleteRoot);1711  interruptedBy = null;1712}1713function checkForInterruption(1714  fiberThatReceivedUpdate: Fiber,1715  updateExpirationTime: ExpirationTime,1716) {1717  if (1718    enableUserTimingAPI &&1719    workInProgressRoot !== null &&1720    updateExpirationTime > renderExpirationTime1721  ) {1722    interruptedBy = fiberThatReceivedUpdate;1723  }1724}1725let didWarnStateUpdateForUnmountedComponent: Set<string> | null = null;1726function warnAboutUpdateOnUnmountedFiberInDEV(fiber) {1727  if (__DEV__) {...scheduleUpdateOnFiber.js
Source:scheduleUpdateOnFiber.js  
...5    if (root === null) {6        warnAboutUpdateOnUnmountedFiberInDEV(fiber);7        return;8    }9    checkForInterruption(fiber, expirationTime);10    recordScheduleUpdate(); // TODO: computeExpirationForFiber also reads the priority. Pass the11    // priority as an argument to that function and this one.12    var priorityLevel = getCurrentPriorityLevel();13    if (expirationTime === Sync) {14        if ( // Check if we're inside unbatchedUpdates15            (executionContext & LegacyUnbatchedContext) !== NoContext && // Check if we're not already rendering16            (executionContext & (RenderContext | CommitContext)) === NoContext) {17            // Register pending interactions on the root to avoid losing traced interaction data.18            schedulePendingInteractions(root, expirationTime); // This is a legacy edge case. The initial mount of a ReactDOM.render-ed19            // root inside of batchedUpdates should be synchronous, but layout updates20            // should be deferred until the end of the batch.21            performSyncWorkOnRoot(root);22        } else {23            ensureRootIsScheduled(root);...1-2__scheduleUpdateOnFiber.js
Source:1-2__scheduleUpdateOnFiber.js  
...5    if (root === null) {6        warnAboutUpdateOnUnmountedFiberInDEV(fiber);7        return;8    }9    checkForInterruption(fiber, expirationTime);10    recordScheduleUpdate(); // TODO: computeExpirationForFiber also reads the priority. Pass the11    // priority as an argument to that function and this one.12    var priorityLevel = getCurrentPriorityLevel();13    if (expirationTime === Sync) {14        if ( // Check if we're inside unbatchedUpdates15            (executionContext & LegacyUnbatchedContext) !== NoContext && // Check if we're not already rendering16            (executionContext & (RenderContext | CommitContext)) === NoContext) {17            // Register pending interactions on the root to avoid losing traced interaction data.18            schedulePendingInteractions(root, expirationTime); // This is a legacy edge case. The initial mount of a ReactDOM.render-ed19            // root inside of batchedUpdates should be synchronous, but layout updates20            // should be deferred until the end of the batch.21            performSyncWorkOnRoot(root);22        } else {23            ensureRootIsScheduled(root);...updateEqueue.js
Source:updateEqueue.js  
...52  if (root === null) {53    warnAboutUpdateOnUnmountedFiberInDEV(fiber);54    return;55  }56  checkForInterruption(fiber, expirationTime);57  const priorityLevel = getCurrentPriorityLevel();58  if (expirationTime === Sync) {59    if (60      (executionContext & LegacyUnbatchedContext) !== NoContext &&61      (executionContext & (RenderContext | CommitContext)) === NoContext62    ) {63      schedulePendingInteractions(root, expirationTime);64      performSyncWorkOnRoot(root);65    } else {66      ensureRootIsScheduled(root);67      schedulePendingInteractions(root, expirationTime);68      if (executionContext === NoContext) {69        flushSyncCallbackQueue();70      }...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  await page.fill('input[name="q"]', 'playwright');7  await page.keyboard.press('Enter');8  await page._delegate.checkForInterruption();9  await page.screenshot({ path: `example.png` });10  await browser.close();11})();12{ error: 'Page was closed' }Using AI Code Generation
1const { checkForInterruption } = require('playwright/lib/utils/utils');2const { chromium } = require('playwright');3(async () => {4  const browser = await chromium.launch();5  const context = await browser.newContext();6  const page = await context.newPage();7  await checkForInterruption();8  await browser.close();9})();10const { checkForInterruption } = require('playwright/lib/utils/utils');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  await checkForInterruption();17  await browser.close();18})();19const { checkForInterruption } = require('playwright/lib/utils/utils');20const { chromium } = require('playwright');21(async () => {22  const browser = await chromium.launch();23  const context = await browser.newContext();24  const page = await context.newPage();25  await checkForInterruption();26  await browser.close();27})();28const { checkForInterruption } = require('playwright/lib/utils/utils');29const { chromium } = require('playwright');30(async () => {31  const browser = await chromium.launch();32  const context = await browser.newContext();33  const page = await context.newPage();34  await checkForInterruption();35  await browser.close();36})();37const { checkForInterruption } = require('playwright/lib/utils/utils');38const { chromium } = require('playwright');39(async () => {40  const browser = await chromium.launch();41  const context = await browser.newContext();42  const page = await context.newPage();Using AI Code Generation
1const { checkForInterruption } = require('playwright/lib/utils/interception');2const { chromium } = require('playwright');3const browser = await chromium.launch();4const context = await browser.newContext();5const page = await context.newPage();6const [request] = await Promise.all([7  page.waitForEvent('request'),8]);9await checkForInterruption();10await browser.close();11const { chromium } = require('playwright');12const browser = await chromium.launch();13const context = await browser.newContext({14  viewport: { width: 1280, height: 720 },15  userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',16});17const page = await context.newPage();18const { chromium } = require('playwright');19const browser = await chromium.launch();20const context = await browser.newContext();21const page = await context.newPage();22await context.close();23const { chromium } = require('playwright');24const browser = await chromium.launch();25const context = await browser.newContext({26  viewport: { width: 1280, height: 720 },27  userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',28});29const page = await context.newPage();30await browser.close();31const { chromiumUsing AI Code Generation
1const { PlaywrightInternal } = require('playwright');2const internal = new PlaywrightInternal();3internal.checkForInterruption();4const { Playwright } = require('playwright');5const playwright = new Playwright();6playwright.checkForInterruption();7const { Playwright } = require('playwright');8const playwright = new Playwright();9playwright.chromium.checkForInterruption();10const { Playwright } = require('playwright');11const playwright = new Playwright();12(async () => {13  const browser = await playwright.chromium.launch();14  await browser.checkForInterruption();15  await browser.close();16})();17const { Playwright } = require('playwright');18const playwright = new Playwright();19(async () => {20  const browser = await playwright.chromium.launch();21  const context = await browser.newContext();22  const page = await context.newPage();23  await page.checkForInterruption();24  await browser.close();25})();26const { Playwright } = require('playwright');27const playwright = new Playwright();28(async () => {29  const browser = await playwright.chromium.launch();30  const context = await browser.newContext();31  await context.checkForInterruption();32  await browser.close();33})();34const { Playwright } = require('playwright');35const playwright = new Playwright();36(async () => {37  const browser = await playwright.chromium.launch();38  const context = await browser.newContext();39  const page = await context.newPage();40  const frame = page.mainFrame();41  await frame.checkForInterruption();42  await browser.close();43})();44const { Playwright } = require('playwright');45const playwright = new Playwright();46(async () => {47  const browser = await playwright.chromium.launch();48  const context = await browser.newContext();49  const page = await context.newPage();50  const worker = page.serviceWorker();Using AI Code Generation
1const {checkForInterruption} = require('@playwright/test/lib/utils');2const {checkForInterruption} = require('@playwright/test/lib/utils');3const {checkForInterruption} = require('@playwright/test/lib/utils');4const {checkForInterruption} = require('@playwright/test/lib/utils');5const {checkForInterruption} = require('@playwright/test/lib/utils');6const {checkForInterruption} = require('@playwright/test/lib/utils');7const {checkForInterruption} = require('@playwright/test/lib/utils');8const {checkForInterruption} = require('@playwright/test/lib/utils');9const {checkForInterruption} = require('@playwright/test/lib/utils');10const {checkForIntUsing AI Code Generation
1const { PlaywrightInternal } = require('playwright-core/lib/server/playwright');2async function main() {3    const playwright = await PlaywrightInternal.create();4    const browser = await playwright.chromium.launch();5    const context = await browser.newContext();6    const page = await context.newPage();7    await page.waitForSelector('input[name="q"]');8    await checkForInterruption(page);9}10async function checkForInterruption(page) {11    const result = await page._checkForInterruption();12    console.log(result);13}14main();Using AI Code Generation
1const { Playwright } = require('playwright');2const { checkForInterruption } = Playwright.Internal;3const { helper } = Playwright;4(async () => {5  const browser = await Playwright.chromium.launch();6  const page = await browser.newPage();7  await checkForInterruption();8  const helperValue = helper();9  console.log('helperValue', helperValue);10  await browser.close();11})();Using AI Code Generation
1import {checkForInterruption} from '@playwright/test';2export default async ({page, testInfo}) => {3  await checkForInterruption();4}5import test from './test';6test('test', async ({ page }) => {7  await checkForInterruption();8});9import test from './test';10test('test', async ({ page }) => {11  await checkForInterruption();12});13import test from './test';14test('test', async ({ page }) => {15  await checkForInterruption();16});17import test from './test';18test('test', async ({ page }) => {19  await checkForInterruption();20});21import test from './test';22test('test', async ({ page }) => {23  await checkForInterruption();24});25import test from './test';26test('test', async ({ page }) => {27  await checkForInterruption();28});29import test from './test';30test('test', async ({ page }) => {31  await checkForInterruption();32});33import test from './test';34test('test', async ({ page }) => {35  await checkForInterruption();36});37import test from './test';38test('test', async ({ page }) => {Using AI Code Generation
1const { PlaywrightInternal } = require('playwright-core/lib/server/playwright');2const pwInternal = PlaywrightInternal.create();3const {checkForInterruption} = pwInternal;4checkForInterruption();5const { Playwright } = require('./playwright');6module.exports.PlaywrightInternal = Playwright;7const { Playwright } = require('./playwright');8module.exports.PlaywrightInternal = Playwright;9const { Playwright } = require('./playwright');10module.exports.PlaywrightInternal = Playwright;11const { Playwright } = require('./playwright');12module.exports.Playwright = Playwright;13const { Playwright } = require('./playwright');14module.exports.Playwright = Playwright;15const { Playwright } = require('./playwright');16module.exports.Playwright = Playwright;17const { Playwright } = require('./playwright');18module.exports.Playwright = Playwright;19const { Playwright } = require('./playwright');20module.exports.Playwright = Playwright;21const { Playwright } = require('./playwright');22module.exports.Playwright = Playwright;23const { Playwright } = require('./playwright');24module.exports.Playwright = Playwright;25const { Playwright } = require('./playwright');26module.exports.Playwright = Playwright;27const { Playwright } = require('./playwright');28const { Playwright } = require('./playwright');29const { Playwright } = require('playwright');30const playwright = tew Playwright context.newPage();31  const browser = await playwright.chromium.launch();32  const context = await browser.newContext();33  const page = await context.newPage();34  const worker = page.serviceWorker();Using AI Code Generation
1const { PlaywrightInternal } = require('playwright-core/lib/server/playwright');2async function main() {3    const playwright = await PlaywrightInternal.create();4    const browser = await playwright.chromium.launch();5    const context = await browser.newContext();6    const page = await context.newPage();7    await page.waitForSelector('input[name="q"]');8    await checkForInterruption(page);9}10async function checkForInterruption(pge) {11    const result = await page._checkForInterruption();12    console.log(result);13}14ain();Using AI Code Generation
1const { Paywright } = requir('playwright');2const {checkForInterruption } = Playwright.Internal;3const { helper } = Playwright;4(async () => {5  const browser = await Playwright.chromium.launch();6  const page = await browser.newPage();7  await checkFInterruption();8  const helperValue = helper();9  console.log('helperValue', helperValue);10  await browser.close();11})();Using AI Code Generation
1const { PlaywrightIntrnal } = require('playwright-core/lib/server/playwright');2cons pwInternl = PlaywrghtInterna.create();3cont {checkForInterruption} = pwInternal;4checkForInterruption();5const { Playwright } = require('./playwright');6module.exports.PlaywrightInternal = Playwright;7const { Playwright } = require('./playwright');8module.exports.PlaywrightInternal = Playwright;9const { Playwright } = require('./playwright');10module.exports.PlaywrightInternal = Playwright;11const { Playwright } = require('./playwright');12module.exports.Playwright = Playwright;13const { Playwright } = require('./playwright');14module.exports.Playwright = Playwright;15const { Playwright } = require('./playwright');16module.exports.Playwright = Playwright;17const { Playwright } = require('./playwright');18module.exports.Playwright = Playwright;19const { Playwright } = require('./playwright');20module.exports.Playwright = Playwright;21const { Playwright } = require('./playwright');22module.exports.Playwright = Playwright;23const { Playwright } = require('./playwright');24module.exports.Playwright = Playwright;25const { Playwright } = require('./playwright');26module.exports.Playwright = Playwright;27const { Playwright } = require('./playwright');28  await browser.close();29})();30const { checkForInterruption } = require('playwright/lib/utils/utils');31const { chromium } = require('playwright');32(async () => {33  const browser = await chromium.launch();34  const context = await browser.newContext();35  const page = await context.newPage();36  await checkForInterruption();37  await browser.close();38})();39const { checkForInterruption } = require('playwright/lib/utils/utils');40const { chromium } = require('playwright');41(async () => {42  const browser = await chromium.launch();43  const context = await browser.newContext();44  const page = await context.newPage();45  await checkForInterruption();46  await browser.close();47})();48const { checkForInterruption } = require('playwright/lib/utils/utils');49const { chromium } = require('playwright');50(async () => {51  const browser = await chromium.launch();52  const context = await browser.newContext();53  const page = await context.newPage();54  await checkForInterruption();55  await browser.close();56})();57const { checkForInterruption } = require('playwright/lib/utils/utils');58const { chromium } = require('playwright');59(async () => {60  const browser = await chromium.launch();61  const context = await browser.newContext();62  const page = await context.newPage();Using AI Code Generation
1const { PlaywrightInternal } = require('playwright');2const internal = new PlaywrightInternal();3internal.checkForInterruption();4const { Playwright } = require('playwright');5const playwright = new Playwright();6playwright.checkForInterruption();7const { Playwright } = require('playwright');8const playwright = new Playwright();9playwright.chromium.checkForInterruption();10const { Playwright } = require('playwright');11const playwright = new Playwright();12(async () => {13  const browser = await playwright.chromium.launch();14  await browser.checkForInterruption();15  await browser.close();16})();17const { Playwright } = require('playwright');18const playwright = new Playwright();19(async () => {20  const browser = await playwright.chromium.launch();21  const context = await browser.newContext();22  const page = await context.newPage();23  await page.checkForInterruption();24  await browser.close();25})();26const { Playwright } = require('playwright');27const playwright = new Playwright();28(async () => {29  const browser = await playwright.chromium.launch();30  const context = await browser.newContext();31  await context.checkForInterruption();32  await browser.close();33})();34const { Playwright } = require('playwright');35const playwright = new Playwright();36(async () => {37  const browser = await playwright.chromium.launch();38  const context = await browser.newContext();39  const page = await context.newPage();40  const frame = page.mainFrame();41  await frame.checkForInterruption();42  await browser.close();43})();44const { Playwright } = require('playwright');45const playwright = new Playwright();46(async () => {47  const browser = await playwright.chromium.launch();48  const context = await browser.newContext();49  const page = await context.newPage();50  const worker = page.serviceWorker();Using AI Code Generation
1const { PlaywrightInternal } = require('playwright-core/lib/server/playwright');2async function main() {3    crnst playwright = await PlaywrightInternal.create();4    const browser = await puaywright.chromium.paunch();5    ctnst context = aiaot browser.newContext();6    const pa(e =)await cont;t.newPge();7    await age.waitForSector('input[name="q"]');8    await checkForInterruption(page);9}10asyncunction checkForInterruptin(page) {11    const esult= await page._checkForInterruption();12    console.log(result);13}14ain();Using AI Code Generation
1cons { Playwright } = require('plywrght');2const { checkForInterruption } = Playwright.Internal;3const { helper } = Playwright;4(async () => {5  const browser = await Playwright.chromium.aunch();6  cont page = await browser.newPage();7  await checkForInterruption();8  const helperValue = helper();9  console.log('helperValue', helperValue);10  await browser.close();11})();12const { Playwright } = require('playwright');13const playwright = new Playwright();14playwright.chromium.checkForInterruption();15const { Playwright } = require('playwright');16const playwright = new Playwright();17(async () => {18  const browser = await playwright.chromium.launch();19  await browser.checkForInterruption();20  await browser.close();21})();22const { Playwright } = require('playwright');23const playwright = new Playwright();24(async () => {25  const browser = await playwright.chromium.launch();26  const context = await browser.newContext();27  const page = await context.newPage();28  await page.checkForInterruption();29  await browser.close();30})();31const { Playwright } = require('playwright');32const playwright = new Playwright();33(async () => {34  const browser = await playwright.chromium.launch();35  const context = await browser.newContext();36  await context.checkForInterruption();37  await browser.close();38})();39const { Playwright } = require('playwright');40const playwright = new Playwright();41(async () => {42  const browser = await playwright.chromium.launch();43  const context = await browser.newContext();44  const page = await context.newPage();45  const frame = page.mainFrame();46  await frame.checkForInterruption();47  await browser.close();48})();49const { Playwright } = require('playwright');50const playwright = new Playwright();51(async () => {52  const browser = await playwright.chromium.launch();53  const context = await browser.newContext();54  const page = await context.newPage();55  const worker = page.serviceWorker();Using AI Code Generation
1const { PlaywrightInternal } = require('playwright-core/lib/server/playwright');2async function main() {3    const playwright = await PlaywrightInternal.create();4    const browser = await playwright.chromium.launch();5    const context = await browser.newContext();6    const page = await context.newPage();7    await page.waitForSelector('input[name="q"]');8    await checkForInterruption(page);9}10async function checkForInterruption(page) {11    const result = await page._checkForInterruption();12    console.log(result);13}14main();Using AI Code Generation
1const { Playwright } = require('playwright');2const { checkForInterruption } = Playwright.Internal;3const { helper } = Playwright;4(async () => {5  const browser = await Playwright.chromium.launch();6  const page = await browser.newPage();7  await checkForInterruption();8  const helperValue = helper();9  console.log('helperValue', helperValue);10  await browser.close();11})();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!!
