Skip to content

Commit e0c7834

Browse files
authored
Retry on error if there's lower priority pending work (facebook#12957)
* Remove enableSuspense flag from PendingPriority module We're going to use this for suspending on error, too. * Retry on error if there's lower priority pending work If an error is thrown, and there's lower priority work, it's possible the lower priority work will fix the error. Retry at the lower priority. If an error is thrown and there's no more work to try, handle the error like we normally do (trigger the nearest error boundary).
1 parent 74b1723 commit e0c7834

File tree

4 files changed

+190
-157
lines changed

4 files changed

+190
-157
lines changed

packages/react-reconciler/src/ReactFiberPendingPriority.js

Lines changed: 117 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -12,30 +12,26 @@ import type {ExpirationTime} from './ReactFiberExpirationTime';
1212

1313
import {NoWork} from './ReactFiberExpirationTime';
1414

15-
import {enableSuspense} from 'shared/ReactFeatureFlags';
16-
1715
// TODO: Offscreen updates
1816

1917
export function markPendingPriorityLevel(
2018
root: FiberRoot,
2119
expirationTime: ExpirationTime,
2220
): void {
23-
if (enableSuspense) {
24-
// Update the latest and earliest pending times
25-
const earliestPendingTime = root.earliestPendingTime;
26-
if (earliestPendingTime === NoWork) {
27-
// No other pending updates.
28-
root.earliestPendingTime = root.latestPendingTime = expirationTime;
21+
// Update the latest and earliest pending times
22+
const earliestPendingTime = root.earliestPendingTime;
23+
if (earliestPendingTime === NoWork) {
24+
// No other pending updates.
25+
root.earliestPendingTime = root.latestPendingTime = expirationTime;
26+
} else {
27+
if (earliestPendingTime > expirationTime) {
28+
// This is the earliest pending update.
29+
root.earliestPendingTime = expirationTime;
2930
} else {
30-
if (earliestPendingTime > expirationTime) {
31-
// This is the earliest pending update.
32-
root.earliestPendingTime = expirationTime;
33-
} else {
34-
const latestPendingTime = root.latestPendingTime;
35-
if (latestPendingTime < expirationTime) {
36-
// This is the latest pending update
37-
root.latestPendingTime = expirationTime;
38-
}
31+
const latestPendingTime = root.latestPendingTime;
32+
if (latestPendingTime < expirationTime) {
33+
// This is the latest pending update
34+
root.latestPendingTime = expirationTime;
3935
}
4036
}
4137
}
@@ -46,114 +42,110 @@ export function markCommittedPriorityLevels(
4642
currentTime: ExpirationTime,
4743
earliestRemainingTime: ExpirationTime,
4844
): void {
49-
if (enableSuspense) {
50-
if (earliestRemainingTime === NoWork) {
51-
// Fast path. There's no remaining work. Clear everything.
52-
root.earliestPendingTime = NoWork;
53-
root.latestPendingTime = NoWork;
54-
root.earliestSuspendedTime = NoWork;
55-
root.latestSuspendedTime = NoWork;
56-
root.latestPingedTime = NoWork;
57-
return;
58-
}
45+
if (earliestRemainingTime === NoWork) {
46+
// Fast path. There's no remaining work. Clear everything.
47+
root.earliestPendingTime = NoWork;
48+
root.latestPendingTime = NoWork;
49+
root.earliestSuspendedTime = NoWork;
50+
root.latestSuspendedTime = NoWork;
51+
root.latestPingedTime = NoWork;
52+
return;
53+
}
5954

60-
// Let's see if the previous latest known pending level was just flushed.
61-
const latestPendingTime = root.latestPendingTime;
62-
if (latestPendingTime !== NoWork) {
63-
if (latestPendingTime < earliestRemainingTime) {
64-
// We've flushed all the known pending levels.
65-
root.earliestPendingTime = root.latestPendingTime = NoWork;
66-
} else {
67-
const earliestPendingTime = root.earliestPendingTime;
68-
if (earliestPendingTime < earliestRemainingTime) {
69-
// We've flushed the earliest known pending level. Set this to the
70-
// latest pending time.
71-
root.earliestPendingTime = root.latestPendingTime;
72-
}
55+
// Let's see if the previous latest known pending level was just flushed.
56+
const latestPendingTime = root.latestPendingTime;
57+
if (latestPendingTime !== NoWork) {
58+
if (latestPendingTime < earliestRemainingTime) {
59+
// We've flushed all the known pending levels.
60+
root.earliestPendingTime = root.latestPendingTime = NoWork;
61+
} else {
62+
const earliestPendingTime = root.earliestPendingTime;
63+
if (earliestPendingTime < earliestRemainingTime) {
64+
// We've flushed the earliest known pending level. Set this to the
65+
// latest pending time.
66+
root.earliestPendingTime = root.latestPendingTime;
7367
}
7468
}
69+
}
7570

76-
// Now let's handle the earliest remaining level in the whole tree. We need to
77-
// decide whether to treat it as a pending level or as suspended. Check
78-
// it falls within the range of known suspended levels.
79-
80-
const earliestSuspendedTime = root.earliestSuspendedTime;
81-
if (earliestSuspendedTime === NoWork) {
82-
// There's no suspended work. Treat the earliest remaining level as a
83-
// pending level.
84-
markPendingPriorityLevel(root, earliestRemainingTime);
85-
return;
86-
}
71+
// Now let's handle the earliest remaining level in the whole tree. We need to
72+
// decide whether to treat it as a pending level or as suspended. Check
73+
// it falls within the range of known suspended levels.
8774

88-
const latestSuspendedTime = root.latestSuspendedTime;
89-
if (earliestRemainingTime > latestSuspendedTime) {
90-
// The earliest remaining level is later than all the suspended work. That
91-
// means we've flushed all the suspended work.
92-
root.earliestSuspendedTime = NoWork;
93-
root.latestSuspendedTime = NoWork;
94-
root.latestPingedTime = NoWork;
95-
96-
// There's no suspended work. Treat the earliest remaining level as a
97-
// pending level.
98-
markPendingPriorityLevel(root, earliestRemainingTime);
99-
return;
100-
}
75+
const earliestSuspendedTime = root.earliestSuspendedTime;
76+
if (earliestSuspendedTime === NoWork) {
77+
// There's no suspended work. Treat the earliest remaining level as a
78+
// pending level.
79+
markPendingPriorityLevel(root, earliestRemainingTime);
80+
return;
81+
}
10182

102-
if (earliestRemainingTime < earliestSuspendedTime) {
103-
// The earliest remaining time is earlier than all the suspended work.
104-
// Treat it as a pending update.
105-
markPendingPriorityLevel(root, earliestRemainingTime);
106-
return;
107-
}
83+
const latestSuspendedTime = root.latestSuspendedTime;
84+
if (earliestRemainingTime > latestSuspendedTime) {
85+
// The earliest remaining level is later than all the suspended work. That
86+
// means we've flushed all the suspended work.
87+
root.earliestSuspendedTime = NoWork;
88+
root.latestSuspendedTime = NoWork;
89+
root.latestPingedTime = NoWork;
90+
91+
// There's no suspended work. Treat the earliest remaining level as a
92+
// pending level.
93+
markPendingPriorityLevel(root, earliestRemainingTime);
94+
return;
95+
}
10896

109-
// The earliest remaining time falls within the range of known suspended
110-
// levels. We should treat this as suspended work.
97+
if (earliestRemainingTime < earliestSuspendedTime) {
98+
// The earliest remaining time is earlier than all the suspended work.
99+
// Treat it as a pending update.
100+
markPendingPriorityLevel(root, earliestRemainingTime);
101+
return;
111102
}
103+
104+
// The earliest remaining time falls within the range of known suspended
105+
// levels. We should treat this as suspended work.
112106
}
113107

114108
export function markSuspendedPriorityLevel(
115109
root: FiberRoot,
116110
suspendedTime: ExpirationTime,
117111
): void {
118-
if (enableSuspense) {
119-
// First, check the known pending levels and update them if needed.
120-
const earliestPendingTime = root.earliestPendingTime;
121-
const latestPendingTime = root.latestPendingTime;
122-
if (earliestPendingTime === suspendedTime) {
123-
if (latestPendingTime === suspendedTime) {
124-
// Both known pending levels were suspended. Clear them.
125-
root.earliestPendingTime = root.latestPendingTime = NoWork;
126-
} else {
127-
// The earliest pending level was suspended. Clear by setting it to the
128-
// latest pending level.
129-
root.earliestPendingTime = latestPendingTime;
130-
}
131-
} else if (latestPendingTime === suspendedTime) {
132-
// The latest pending level was suspended. Clear by setting it to the
112+
// First, check the known pending levels and update them if needed.
113+
const earliestPendingTime = root.earliestPendingTime;
114+
const latestPendingTime = root.latestPendingTime;
115+
if (earliestPendingTime === suspendedTime) {
116+
if (latestPendingTime === suspendedTime) {
117+
// Both known pending levels were suspended. Clear them.
118+
root.earliestPendingTime = root.latestPendingTime = NoWork;
119+
} else {
120+
// The earliest pending level was suspended. Clear by setting it to the
133121
// latest pending level.
134-
root.latestPendingTime = earliestPendingTime;
122+
root.earliestPendingTime = latestPendingTime;
135123
}
124+
} else if (latestPendingTime === suspendedTime) {
125+
// The latest pending level was suspended. Clear by setting it to the
126+
// latest pending level.
127+
root.latestPendingTime = earliestPendingTime;
128+
}
136129

137-
// Next, if we're working on the lowest known suspended level, clear the ping.
138-
// TODO: What if a promise suspends and pings before the root completes?
139-
const latestSuspendedTime = root.latestSuspendedTime;
140-
if (latestSuspendedTime === suspendedTime) {
141-
root.latestPingedTime = NoWork;
142-
}
130+
// Next, if we're working on the lowest known suspended level, clear the ping.
131+
// TODO: What if a promise suspends and pings before the root completes?
132+
const latestSuspendedTime = root.latestSuspendedTime;
133+
if (latestSuspendedTime === suspendedTime) {
134+
root.latestPingedTime = NoWork;
135+
}
143136

144-
// Finally, update the known suspended levels.
145-
const earliestSuspendedTime = root.earliestSuspendedTime;
146-
if (earliestSuspendedTime === NoWork) {
147-
// No other suspended levels.
148-
root.earliestSuspendedTime = root.latestSuspendedTime = suspendedTime;
149-
} else {
150-
if (earliestSuspendedTime > suspendedTime) {
151-
// This is the earliest suspended level.
152-
root.earliestSuspendedTime = suspendedTime;
153-
} else if (latestSuspendedTime < suspendedTime) {
154-
// This is the latest suspended level
155-
root.latestSuspendedTime = suspendedTime;
156-
}
137+
// Finally, update the known suspended levels.
138+
const earliestSuspendedTime = root.earliestSuspendedTime;
139+
if (earliestSuspendedTime === NoWork) {
140+
// No other suspended levels.
141+
root.earliestSuspendedTime = root.latestSuspendedTime = suspendedTime;
142+
} else {
143+
if (earliestSuspendedTime > suspendedTime) {
144+
// This is the earliest suspended level.
145+
root.earliestSuspendedTime = suspendedTime;
146+
} else if (latestSuspendedTime < suspendedTime) {
147+
// This is the latest suspended level
148+
root.latestSuspendedTime = suspendedTime;
157149
}
158150
}
159151
}
@@ -162,35 +154,29 @@ export function markPingedPriorityLevel(
162154
root: FiberRoot,
163155
pingedTime: ExpirationTime,
164156
): void {
165-
if (enableSuspense) {
166-
const latestSuspendedTime = root.latestSuspendedTime;
167-
if (latestSuspendedTime !== NoWork && latestSuspendedTime <= pingedTime) {
168-
const latestPingedTime = root.latestPingedTime;
169-
if (latestPingedTime === NoWork || latestPingedTime < pingedTime) {
170-
root.latestPingedTime = pingedTime;
171-
}
157+
const latestSuspendedTime = root.latestSuspendedTime;
158+
if (latestSuspendedTime !== NoWork && latestSuspendedTime <= pingedTime) {
159+
const latestPingedTime = root.latestPingedTime;
160+
if (latestPingedTime === NoWork || latestPingedTime < pingedTime) {
161+
root.latestPingedTime = pingedTime;
172162
}
173163
}
174164
}
175165

176166
export function findNextPendingPriorityLevel(root: FiberRoot): ExpirationTime {
177-
if (enableSuspense) {
178-
const earliestSuspendedTime = root.earliestSuspendedTime;
179-
const earliestPendingTime = root.earliestPendingTime;
180-
if (earliestSuspendedTime === NoWork) {
181-
// Fast path. There's no suspended work.
182-
return earliestPendingTime;
183-
}
184-
185-
// First, check if there's known pending work.
186-
if (earliestPendingTime !== NoWork) {
187-
return earliestPendingTime;
188-
}
167+
const earliestSuspendedTime = root.earliestSuspendedTime;
168+
const earliestPendingTime = root.earliestPendingTime;
169+
if (earliestSuspendedTime === NoWork) {
170+
// Fast path. There's no suspended work.
171+
return earliestPendingTime;
172+
}
189173

190-
// Finally, if a suspended level was pinged, work on that. Otherwise there's
191-
// nothing to work on.
192-
return root.latestPingedTime;
193-
} else {
194-
return root.current.expirationTime;
174+
// First, check if there's known pending work.
175+
if (earliestPendingTime !== NoWork) {
176+
return earliestPendingTime;
195177
}
178+
179+
// Finally, if a suspended level was pinged, work on that. Otherwise there's
180+
// nothing to work on.
181+
return root.latestPingedTime;
196182
}

0 commit comments

Comments
 (0)