How to use checkForInterruption method in Playwright Internal

Best JavaScript code snippet using playwright-internal

ReactFiberWorkLoop.js

Source:ReactFiberWorkLoop.js Github

copy

Full Screen

...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) {...

Full Screen

Full Screen

ReactFiberScheduler.js

Source:ReactFiberScheduler.js Github

copy

Full Screen

...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__) {...

Full Screen

Full Screen

ReactFiberScheduler.new.js

Source:ReactFiberScheduler.new.js Github

copy

Full Screen

...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__) {...

Full Screen

Full Screen

scheduleUpdateOnFiber.js

Source:scheduleUpdateOnFiber.js Github

copy

Full Screen

...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);...

Full Screen

Full Screen

1-2__scheduleUpdateOnFiber.js

Source:1-2__scheduleUpdateOnFiber.js Github

copy

Full Screen

...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);...

Full Screen

Full Screen

updateEqueue.js

Source:updateEqueue.js Github

copy

Full Screen

...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 }...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

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' }

Full Screen

Using AI Code Generation

copy

Full Screen

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();

Full Screen

Using AI Code Generation

copy

Full Screen

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

Full Screen

Using AI Code Generation

copy

Full Screen

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();

Full Screen

Using AI Code Generation

copy

Full Screen

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

Full Screen

Using AI Code Generation

copy

Full Screen

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();

Full Screen

Using AI Code Generation

copy

Full Screen

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})();

Full Screen

Using AI Code Generation

copy

Full Screen

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 }) => {

Full Screen

Using AI Code Generation

copy

Full Screen

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();

Full Screen

Using AI Code Generation

copy

Full Screen

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();

Full Screen

Using AI Code Generation

copy

Full Screen

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})();

Full Screen

Using AI Code Generation

copy

Full Screen

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();

Full Screen

Using AI Code Generation

copy

Full Screen

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();

Full Screen

Using AI Code Generation

copy

Full Screen

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();

Full Screen

Using AI Code Generation

copy

Full Screen

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();

Full Screen

Using AI Code Generation

copy

Full Screen

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();

Full Screen

Using AI Code Generation

copy

Full Screen

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})();

Full Screen

Playwright tutorial

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.

Chapters:

  1. What is Playwright : Playwright is comparatively new but has gained good popularity. Get to know some history of the Playwright with some interesting facts connected with it.
  2. How To Install Playwright : Learn in detail about what basic configuration and dependencies are required for installing Playwright and run a test. Get a step-by-step direction for installing the Playwright automation framework.
  3. Playwright Futuristic Features: Launched in 2020, Playwright gained huge popularity quickly because of some obliging features such as Playwright Test Generator and Inspector, Playwright Reporter, Playwright auto-waiting mechanism and etc. Read up on those features to master Playwright testing.
  4. What is Component Testing: Component testing in Playwright is a unique feature that allows a tester to test a single component of a web application without integrating them with other elements. Learn how to perform Component testing on the Playwright automation framework.
  5. Inputs And Buttons In Playwright: Every website has Input boxes and buttons; learn about testing inputs and buttons with different scenarios and examples.
  6. Functions and Selectors in Playwright: Learn how to launch the Chromium browser with Playwright. Also, gain a better understanding of some important functions like “BrowserContext,” which allows you to run multiple browser sessions, and “newPage” which interacts with a page.
  7. Handling Alerts and Dropdowns in Playwright : Playwright interact with different types of alerts and pop-ups, such as simple, confirmation, and prompt, and different types of dropdowns, such as single selector and multi-selector get your hands-on with handling alerts and dropdown in Playright testing.
  8. Playwright vs Puppeteer: Get to know about the difference between two testing frameworks and how they are different than one another, which browsers they support, and what features they provide.
  9. Run Playwright Tests on LambdaTest: Playwright testing with LambdaTest leverages test performance to the utmost. You can run multiple Playwright tests in Parallel with the LammbdaTest test cloud. Get a step-by-step guide to run your Playwright test on the LambdaTest platform.
  10. Playwright Python Tutorial: Playwright automation framework support all major languages such as Python, JavaScript, TypeScript, .NET and etc. However, there are various advantages to Python end-to-end testing with Playwright because of its versatile utility. Get the hang of Playwright python testing with this chapter.
  11. Playwright End To End Testing Tutorial: Get your hands on with Playwright end-to-end testing and learn to use some exciting features such as TraceViewer, Debugging, Networking, Component testing, Visual testing, and many more.
  12. Playwright Video Tutorial: Watch the video tutorials on Playwright testing from experts and get a consecutive in-depth explanation of Playwright automation testing.

Run Playwright Internal automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful