Skip to content

Commit d2c0ab1

Browse files
committed
In work loop, add enum of reasons for suspending
This is a pure refactor, no change to behavior. When a component throws, the work loop can handle that in one of several ways — unwind immediately, wait for microtasks, and so on. I'm about to add another one, too. So I've changed the variable that tracks whether the work loop is suspended from a boolean (workInProgressIsSuspended) to an enum (workInProgressSuspendedReason).
1 parent 5450dd4 commit d2c0ab1

File tree

2 files changed

+66
-40
lines changed

2 files changed

+66
-40
lines changed

packages/react-reconciler/src/ReactFiberWorkLoop.new.js

+33-20
Original file line numberDiff line numberDiff line change
@@ -310,11 +310,18 @@ let workInProgress: Fiber | null = null;
310310
// The lanes we're rendering
311311
let workInProgressRootRenderLanes: Lanes = NoLanes;
312312

313+
opaque type SuspendedReason = 0 | 1 | 2 | 3 | 4;
314+
const NotSuspended: SuspendedReason = 0;
315+
const SuspendedOnError: SuspendedReason = 1;
316+
// const SuspendedOnData: SuspendedReason = 2;
317+
const SuspendedOnImmediate: SuspendedReason = 3;
318+
const SuspendedAndReadyToUnwind: SuspendedReason = 4;
319+
313320
// When this is true, the work-in-progress fiber just suspended (or errored) and
314321
// we've yet to unwind the stack. In some cases, we may yield to the main thread
315322
// after this happens. If the fiber is pinged before we resume, we can retry
316323
// immediately instead of unwinding the stack.
317-
let workInProgressIsSuspended: boolean = false;
324+
let workInProgressSuspendedReason: SuspendedReason = NotSuspended;
318325
let workInProgressThrownValue: mixed = null;
319326
let workInProgressSuspendedThenableState: ThenableState | null = null;
320327

@@ -1676,9 +1683,10 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes): Fiber {
16761683
}
16771684

16781685
if (workInProgress !== null) {
1679-
let interruptedWork = workInProgressIsSuspended
1680-
? workInProgress
1681-
: workInProgress.return;
1686+
let interruptedWork =
1687+
workInProgressSuspendedReason === NotSuspended
1688+
? workInProgress.return
1689+
: workInProgress;
16821690
while (interruptedWork !== null) {
16831691
const current = interruptedWork.alternate;
16841692
unwindInterruptedWork(
@@ -1693,7 +1701,7 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes): Fiber {
16931701
const rootWorkInProgress = createWorkInProgress(root.current, null);
16941702
workInProgress = rootWorkInProgress;
16951703
workInProgressRootRenderLanes = renderLanes = lanes;
1696-
workInProgressIsSuspended = false;
1704+
workInProgressSuspendedReason = NotSuspended;
16971705
workInProgressThrownValue = null;
16981706
workInProgressSuspendedThenableState = null;
16991707
workInProgressRootDidAttachPingListener = false;
@@ -1732,17 +1740,27 @@ function handleThrow(root, thrownValue): void {
17321740
// deprecate the old API in favor of `use`.
17331741
thrownValue = getSuspendedThenable();
17341742
workInProgressSuspendedThenableState = getThenableStateAfterSuspending();
1743+
workInProgressSuspendedReason = SuspendedOnImmediate;
17351744
} else {
17361745
// This is a regular error. If something earlier in the component already
17371746
// suspended, we must clear the thenable state to unblock the work loop.
17381747
workInProgressSuspendedThenableState = null;
1748+
1749+
const isWakeable =
1750+
thrownValue !== null &&
1751+
typeof thrownValue === 'object' &&
1752+
// $FlowFixMe[method-unbinding]
1753+
typeof thrownValue.then === 'function';
1754+
1755+
workInProgressSuspendedReason = isWakeable
1756+
? // A wakeable object was thrown by a legacy Suspense implementation.
1757+
// This has slightly different behavior than suspending with `use`.
1758+
SuspendedAndReadyToUnwind
1759+
: // This is a regular error. If something earlier in the component already
1760+
// suspended, we must clear the thenable state to unblock the work loop.
1761+
SuspendedOnError;
17391762
}
17401763

1741-
// Setting this to `true` tells the work loop to unwind the stack instead
1742-
// of entering the begin phase. It's called "suspended" because it usually
1743-
// happens because of Suspense, but it also applies to errors. Think of it
1744-
// as suspending the execution of the work loop.
1745-
workInProgressIsSuspended = true;
17461764
workInProgressThrownValue = thrownValue;
17471765

17481766
const erroredWork = workInProgress;
@@ -1762,12 +1780,7 @@ function handleThrow(root, thrownValue): void {
17621780

17631781
if (enableSchedulingProfiler) {
17641782
markComponentRenderStopped();
1765-
if (
1766-
thrownValue !== null &&
1767-
typeof thrownValue === 'object' &&
1768-
// $FlowFixMe[method-unbinding]
1769-
typeof thrownValue.then === 'function'
1770-
) {
1783+
if (workInProgressSuspendedReason !== SuspendedOnError) {
17711784
const wakeable: Wakeable = (thrownValue: any);
17721785
markComponentSuspended(
17731786
erroredWork,
@@ -1968,11 +1981,11 @@ function renderRootSync(root: FiberRoot, lanes: Lanes) {
19681981
function workLoopSync() {
19691982
// Perform work without checking if we need to yield between fiber.
19701983

1971-
if (workInProgressIsSuspended) {
1984+
if (workInProgressSuspendedReason !== NotSuspended) {
19721985
// The current work-in-progress was already attempted. We need to unwind
19731986
// it before we continue the normal work loop.
19741987
const thrownValue = workInProgressThrownValue;
1975-
workInProgressIsSuspended = false;
1988+
workInProgressSuspendedReason = NotSuspended;
19761989
workInProgressThrownValue = null;
19771990
if (workInProgress !== null) {
19781991
resumeSuspendedUnitOfWork(workInProgress, thrownValue);
@@ -2079,11 +2092,11 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
20792092
function workLoopConcurrent() {
20802093
// Perform work until Scheduler asks us to yield
20812094

2082-
if (workInProgressIsSuspended) {
2095+
if (workInProgressSuspendedReason !== NotSuspended) {
20832096
// The current work-in-progress was already attempted. We need to unwind
20842097
// it before we continue the normal work loop.
20852098
const thrownValue = workInProgressThrownValue;
2086-
workInProgressIsSuspended = false;
2099+
workInProgressSuspendedReason = NotSuspended;
20872100
workInProgressThrownValue = null;
20882101
if (workInProgress !== null) {
20892102
resumeSuspendedUnitOfWork(workInProgress, thrownValue);

packages/react-reconciler/src/ReactFiberWorkLoop.old.js

+33-20
Original file line numberDiff line numberDiff line change
@@ -310,11 +310,18 @@ let workInProgress: Fiber | null = null;
310310
// The lanes we're rendering
311311
let workInProgressRootRenderLanes: Lanes = NoLanes;
312312

313+
opaque type SuspendedReason = 0 | 1 | 2 | 3 | 4;
314+
const NotSuspended: SuspendedReason = 0;
315+
const SuspendedOnError: SuspendedReason = 1;
316+
// const SuspendedOnData: SuspendedReason = 2;
317+
const SuspendedOnImmediate: SuspendedReason = 3;
318+
const SuspendedAndReadyToUnwind: SuspendedReason = 4;
319+
313320
// When this is true, the work-in-progress fiber just suspended (or errored) and
314321
// we've yet to unwind the stack. In some cases, we may yield to the main thread
315322
// after this happens. If the fiber is pinged before we resume, we can retry
316323
// immediately instead of unwinding the stack.
317-
let workInProgressIsSuspended: boolean = false;
324+
let workInProgressSuspendedReason: SuspendedReason = NotSuspended;
318325
let workInProgressThrownValue: mixed = null;
319326
let workInProgressSuspendedThenableState: ThenableState | null = null;
320327

@@ -1676,9 +1683,10 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes): Fiber {
16761683
}
16771684

16781685
if (workInProgress !== null) {
1679-
let interruptedWork = workInProgressIsSuspended
1680-
? workInProgress
1681-
: workInProgress.return;
1686+
let interruptedWork =
1687+
workInProgressSuspendedReason === NotSuspended
1688+
? workInProgress.return
1689+
: workInProgress;
16821690
while (interruptedWork !== null) {
16831691
const current = interruptedWork.alternate;
16841692
unwindInterruptedWork(
@@ -1693,7 +1701,7 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes): Fiber {
16931701
const rootWorkInProgress = createWorkInProgress(root.current, null);
16941702
workInProgress = rootWorkInProgress;
16951703
workInProgressRootRenderLanes = renderLanes = lanes;
1696-
workInProgressIsSuspended = false;
1704+
workInProgressSuspendedReason = NotSuspended;
16971705
workInProgressThrownValue = null;
16981706
workInProgressSuspendedThenableState = null;
16991707
workInProgressRootDidAttachPingListener = false;
@@ -1732,17 +1740,27 @@ function handleThrow(root, thrownValue): void {
17321740
// deprecate the old API in favor of `use`.
17331741
thrownValue = getSuspendedThenable();
17341742
workInProgressSuspendedThenableState = getThenableStateAfterSuspending();
1743+
workInProgressSuspendedReason = SuspendedOnImmediate;
17351744
} else {
17361745
// This is a regular error. If something earlier in the component already
17371746
// suspended, we must clear the thenable state to unblock the work loop.
17381747
workInProgressSuspendedThenableState = null;
1748+
1749+
const isWakeable =
1750+
thrownValue !== null &&
1751+
typeof thrownValue === 'object' &&
1752+
// $FlowFixMe[method-unbinding]
1753+
typeof thrownValue.then === 'function';
1754+
1755+
workInProgressSuspendedReason = isWakeable
1756+
? // A wakeable object was thrown by a legacy Suspense implementation.
1757+
// This has slightly different behavior than suspending with `use`.
1758+
SuspendedAndReadyToUnwind
1759+
: // This is a regular error. If something earlier in the component already
1760+
// suspended, we must clear the thenable state to unblock the work loop.
1761+
SuspendedOnError;
17391762
}
17401763

1741-
// Setting this to `true` tells the work loop to unwind the stack instead
1742-
// of entering the begin phase. It's called "suspended" because it usually
1743-
// happens because of Suspense, but it also applies to errors. Think of it
1744-
// as suspending the execution of the work loop.
1745-
workInProgressIsSuspended = true;
17461764
workInProgressThrownValue = thrownValue;
17471765

17481766
const erroredWork = workInProgress;
@@ -1762,12 +1780,7 @@ function handleThrow(root, thrownValue): void {
17621780

17631781
if (enableSchedulingProfiler) {
17641782
markComponentRenderStopped();
1765-
if (
1766-
thrownValue !== null &&
1767-
typeof thrownValue === 'object' &&
1768-
// $FlowFixMe[method-unbinding]
1769-
typeof thrownValue.then === 'function'
1770-
) {
1783+
if (workInProgressSuspendedReason !== SuspendedOnError) {
17711784
const wakeable: Wakeable = (thrownValue: any);
17721785
markComponentSuspended(
17731786
erroredWork,
@@ -1968,11 +1981,11 @@ function renderRootSync(root: FiberRoot, lanes: Lanes) {
19681981
function workLoopSync() {
19691982
// Perform work without checking if we need to yield between fiber.
19701983

1971-
if (workInProgressIsSuspended) {
1984+
if (workInProgressSuspendedReason !== NotSuspended) {
19721985
// The current work-in-progress was already attempted. We need to unwind
19731986
// it before we continue the normal work loop.
19741987
const thrownValue = workInProgressThrownValue;
1975-
workInProgressIsSuspended = false;
1988+
workInProgressSuspendedReason = NotSuspended;
19761989
workInProgressThrownValue = null;
19771990
if (workInProgress !== null) {
19781991
resumeSuspendedUnitOfWork(workInProgress, thrownValue);
@@ -2079,11 +2092,11 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
20792092
function workLoopConcurrent() {
20802093
// Perform work until Scheduler asks us to yield
20812094

2082-
if (workInProgressIsSuspended) {
2095+
if (workInProgressSuspendedReason !== NotSuspended) {
20832096
// The current work-in-progress was already attempted. We need to unwind
20842097
// it before we continue the normal work loop.
20852098
const thrownValue = workInProgressThrownValue;
2086-
workInProgressIsSuspended = false;
2099+
workInProgressSuspendedReason = NotSuspended;
20872100
workInProgressThrownValue = null;
20882101
if (workInProgress !== null) {
20892102
resumeSuspendedUnitOfWork(workInProgress, thrownValue);

0 commit comments

Comments
 (0)