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 { chromium
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 {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 {checkForInt
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})();
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!!