@@ -1936,6 +1936,24 @@ function renderRootSync(root: FiberRoot, lanes: Lanes) {
1936
1936
1937
1937
do {
1938
1938
try {
1939
+ if ( workInProgressSuspendedReason !== NotSuspended ) {
1940
+ if ( workInProgress !== null ) {
1941
+ // The current work-in-progress was already attempted. We need to unwind
1942
+ // it before we continue the normal work loop.
1943
+ const unitOfWork = workInProgress ;
1944
+ const thrownValue = workInProgressThrownValue ;
1945
+ workInProgressSuspendedReason = NotSuspended ;
1946
+ workInProgressThrownValue = null ;
1947
+ const wasPinged =
1948
+ workInProgressSuspendedThenableState !== null &&
1949
+ isThenableStateResolved ( workInProgressSuspendedThenableState ) ;
1950
+ if ( wasPinged ) {
1951
+ replaySuspendedUnitOfWork ( unitOfWork , thrownValue ) ;
1952
+ } else {
1953
+ unwindSuspendedUnitOfWork ( unitOfWork , thrownValue ) ;
1954
+ }
1955
+ }
1956
+ }
1939
1957
workLoopSync ( ) ;
1940
1958
break ;
1941
1959
} catch ( thrownValue ) {
@@ -1980,18 +1998,6 @@ function renderRootSync(root: FiberRoot, lanes: Lanes) {
1980
1998
/** @noinline */
1981
1999
function workLoopSync ( ) {
1982
2000
// Perform work without checking if we need to yield between fiber.
1983
-
1984
- if ( workInProgressSuspendedReason !== NotSuspended ) {
1985
- // The current work-in-progress was already attempted. We need to unwind
1986
- // it before we continue the normal work loop.
1987
- const thrownValue = workInProgressThrownValue ;
1988
- workInProgressSuspendedReason = NotSuspended ;
1989
- workInProgressThrownValue = null ;
1990
- if ( workInProgress !== null ) {
1991
- resumeSuspendedUnitOfWork ( workInProgress , thrownValue ) ;
1992
- }
1993
- }
1994
-
1995
2001
while ( workInProgress !== null ) {
1996
2002
performUnitOfWork ( workInProgress ) ;
1997
2003
}
@@ -2039,6 +2045,24 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
2039
2045
2040
2046
do {
2041
2047
try {
2048
+ if ( workInProgressSuspendedReason !== NotSuspended ) {
2049
+ if ( workInProgress !== null ) {
2050
+ // The current work-in-progress was already attempted. We need to unwind
2051
+ // it before we continue the normal work loop.
2052
+ const unitOfWork = workInProgress ;
2053
+ const thrownValue = workInProgressThrownValue ;
2054
+ workInProgressSuspendedReason = NotSuspended ;
2055
+ workInProgressThrownValue = null ;
2056
+ const wasPinged =
2057
+ workInProgressSuspendedThenableState !== null &&
2058
+ isThenableStateResolved ( workInProgressSuspendedThenableState ) ;
2059
+ if ( wasPinged ) {
2060
+ replaySuspendedUnitOfWork ( unitOfWork , thrownValue ) ;
2061
+ } else {
2062
+ unwindSuspendedUnitOfWork ( unitOfWork , thrownValue ) ;
2063
+ }
2064
+ }
2065
+ }
2042
2066
workLoopConcurrent ( ) ;
2043
2067
break ;
2044
2068
} catch ( thrownValue ) {
@@ -2091,18 +2115,6 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
2091
2115
/** @noinline */
2092
2116
function workLoopConcurrent ( ) {
2093
2117
// Perform work until Scheduler asks us to yield
2094
-
2095
- if ( workInProgressSuspendedReason !== NotSuspended ) {
2096
- // The current work-in-progress was already attempted. We need to unwind
2097
- // it before we continue the normal work loop.
2098
- const thrownValue = workInProgressThrownValue ;
2099
- workInProgressSuspendedReason = NotSuspended ;
2100
- workInProgressThrownValue = null ;
2101
- if ( workInProgress !== null ) {
2102
- resumeSuspendedUnitOfWork ( workInProgress , thrownValue ) ;
2103
- }
2104
- }
2105
-
2106
2118
while ( workInProgress !== null && ! shouldYield ( ) ) {
2107
2119
// $FlowFixMe[incompatible-call] found when upgrading Flow
2108
2120
performUnitOfWork ( workInProgress ) ;
@@ -2137,69 +2149,15 @@ function performUnitOfWork(unitOfWork: Fiber): void {
2137
2149
ReactCurrentOwner . current = null ;
2138
2150
}
2139
2151
2140
- function resumeSuspendedUnitOfWork (
2152
+ function replaySuspendedUnitOfWork (
2141
2153
unitOfWork : Fiber ,
2142
2154
thrownValue : mixed ,
2143
2155
) : void {
2144
- // This is a fork of performUnitOfWork specifcally for resuming a fiber that
2145
- // just suspended. In some cases, we may choose to retry the fiber immediately
2146
- // instead of unwinding the stack. It's a separate function to keep the
2147
- // additional logic out of the work loop's hot path.
2148
-
2149
- const wasPinged =
2150
- workInProgressSuspendedThenableState !== null &&
2151
- isThenableStateResolved ( workInProgressSuspendedThenableState ) ;
2152
-
2153
- if ( ! wasPinged ) {
2154
- // The thenable wasn't pinged. Return to the normal work loop. This will
2155
- // unwind the stack, and potentially result in showing a fallback.
2156
- workInProgressSuspendedThenableState = null ;
2157
-
2158
- const returnFiber = unitOfWork . return ;
2159
- if ( returnFiber === null || workInProgressRoot === null ) {
2160
- // Expected to be working on a non-root fiber. This is a fatal error
2161
- // because there's no ancestor that can handle it; the root is
2162
- // supposed to capture all errors that weren't caught by an error
2163
- // boundary.
2164
- workInProgressRootExitStatus = RootFatalErrored ;
2165
- workInProgressRootFatalError = thrownValue ;
2166
- // Set `workInProgress` to null. This represents advancing to the next
2167
- // sibling, or the parent if there are no siblings. But since the root
2168
- // has no siblings nor a parent, we set it to null. Usually this is
2169
- // handled by `completeUnitOfWork` or `unwindWork`, but since we're
2170
- // intentionally not calling those, we need set it here.
2171
- // TODO: Consider calling `unwindWork` to pop the contexts.
2172
- workInProgress = null ;
2173
- return ;
2174
- }
2175
-
2176
- try {
2177
- // Find and mark the nearest Suspense or error boundary that can handle
2178
- // this "exception".
2179
- throwException (
2180
- workInProgressRoot ,
2181
- returnFiber ,
2182
- unitOfWork ,
2183
- thrownValue ,
2184
- workInProgressRootRenderLanes ,
2185
- ) ;
2186
- } catch ( error ) {
2187
- // We had trouble processing the error. An example of this happening is
2188
- // when accessing the `componentDidCatch` property of an error boundary
2189
- // throws an error. A weird edge case. There's a regression test for this.
2190
- // To prevent an infinite loop, bubble the error up to the next parent.
2191
- workInProgress = returnFiber ;
2192
- throw error ;
2193
- }
2194
-
2195
- // Return to the normal work loop.
2196
- completeUnitOfWork ( unitOfWork ) ;
2197
- return ;
2198
- }
2199
-
2200
- // The work-in-progress was immediately pinged. Instead of unwinding the
2201
- // stack and potentially showing a fallback, unwind only the last stack frame,
2202
- // reset the fiber, and try rendering it again.
2156
+ // This is a fork of performUnitOfWork specifcally for replaying a fiber that
2157
+ // just suspended.
2158
+ //
2159
+ // Instead of unwinding the stack and potentially showing a fallback, unwind
2160
+ // only the last stack frame, reset the fiber, and try rendering it again.
2203
2161
const current = unitOfWork . alternate ;
2204
2162
unwindInterruptedWork ( current , unitOfWork , workInProgressRootRenderLanes ) ;
2205
2163
unitOfWork = workInProgress = resetWorkInProgress ( unitOfWork , renderLanes ) ;
@@ -2232,6 +2190,55 @@ function resumeSuspendedUnitOfWork(
2232
2190
ReactCurrentOwner . current = null ;
2233
2191
}
2234
2192
2193
+ function unwindSuspendedUnitOfWork ( unitOfWork : Fiber , thrownValue : mixed ) {
2194
+ // This is a fork of performUnitOfWork specifcally for unwinding a fiber
2195
+ // that threw an exception.
2196
+ //
2197
+ // Return to the normal work loop. This will unwind the stack, and potentially
2198
+ // result in showing a fallback.
2199
+ workInProgressSuspendedThenableState = null ;
2200
+
2201
+ const returnFiber = unitOfWork . return ;
2202
+ if ( returnFiber === null || workInProgressRoot === null ) {
2203
+ // Expected to be working on a non-root fiber. This is a fatal error
2204
+ // because there's no ancestor that can handle it; the root is
2205
+ // supposed to capture all errors that weren't caught by an error
2206
+ // boundary.
2207
+ workInProgressRootExitStatus = RootFatalErrored ;
2208
+ workInProgressRootFatalError = thrownValue ;
2209
+ // Set `workInProgress` to null. This represents advancing to the next
2210
+ // sibling, or the parent if there are no siblings. But since the root
2211
+ // has no siblings nor a parent, we set it to null. Usually this is
2212
+ // handled by `completeUnitOfWork` or `unwindWork`, but since we're
2213
+ // intentionally not calling those, we need set it here.
2214
+ // TODO: Consider calling `unwindWork` to pop the contexts.
2215
+ workInProgress = null ;
2216
+ return ;
2217
+ }
2218
+
2219
+ try {
2220
+ // Find and mark the nearest Suspense or error boundary that can handle
2221
+ // this "exception".
2222
+ throwException (
2223
+ workInProgressRoot ,
2224
+ returnFiber ,
2225
+ unitOfWork ,
2226
+ thrownValue ,
2227
+ workInProgressRootRenderLanes ,
2228
+ ) ;
2229
+ } catch ( error ) {
2230
+ // We had trouble processing the error. An example of this happening is
2231
+ // when accessing the `componentDidCatch` property of an error boundary
2232
+ // throws an error. A weird edge case. There's a regression test for this.
2233
+ // To prevent an infinite loop, bubble the error up to the next parent.
2234
+ workInProgress = returnFiber ;
2235
+ throw error ;
2236
+ }
2237
+
2238
+ // Return to the normal work loop.
2239
+ completeUnitOfWork ( unitOfWork ) ;
2240
+ }
2241
+
2235
2242
export function getSuspendedThenableState ( ) : ThenableState | null {
2236
2243
return workInProgressSuspendedThenableState ;
2237
2244
}
0 commit comments