Skip to content

Commit 7c4b73f

Browse files
committed
Resolve deep cycle
1 parent 7d795d6 commit 7c4b73f

File tree

1 file changed

+70
-4
lines changed

1 file changed

+70
-4
lines changed

packages/react-client/src/ReactFlightClient.js

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ function wakeChunk<T>(
422422
}
423423
}
424424

425-
function rejectChunk<T>(
425+
function rejectChunk(
426426
listeners: Array<InitializationReference | (mixed => mixed)>,
427427
error: mixed,
428428
): void {
@@ -436,6 +436,33 @@ function rejectChunk<T>(
436436
}
437437
}
438438

439+
function resolveBlockedCycle<T>(
440+
resolvedChunk: SomeChunk<T>,
441+
reference: InitializationReference,
442+
): null | InitializationHandler {
443+
const referencedChunk = reference.handler.chunk;
444+
if (referencedChunk === null) {
445+
return null;
446+
}
447+
if (referencedChunk === resolvedChunk) {
448+
// We found the cycle. We can resolve the blocked cycle now.
449+
return reference.handler;
450+
}
451+
const resolveListeners = referencedChunk.value;
452+
if (resolveListeners !== null) {
453+
for (let i = 0; i < resolveListeners.length; i++) {
454+
const listener = resolveListeners[i];
455+
if (typeof listener !== 'function') {
456+
const foundHandler = resolveBlockedCycle(resolvedChunk, listener);
457+
if (foundHandler !== null) {
458+
return foundHandler;
459+
}
460+
}
461+
}
462+
}
463+
return null;
464+
}
465+
439466
function wakeChunkIfInitialized<T>(
440467
chunk: SomeChunk<T>,
441468
resolveListeners: Array<InitializationReference | (T => mixed)>,
@@ -445,8 +472,32 @@ function wakeChunkIfInitialized<T>(
445472
case INITIALIZED:
446473
wakeChunk(resolveListeners, chunk.value);
447474
break;
448-
case PENDING:
449475
case BLOCKED:
476+
// It is possible that we're blocked on our own chunk if it's a cycle.
477+
// Before adding back the listeners to the chunk, let's check if it would
478+
// result in a cycle.
479+
for (let i = 0; i < resolveListeners.length; i++) {
480+
const listener = resolveListeners[i];
481+
if (typeof listener !== 'function') {
482+
const reference: InitializationReference = listener;
483+
const cyclicHandler = resolveBlockedCycle(chunk, reference);
484+
if (cyclicHandler !== null) {
485+
// This reference points back to this chunk. We can resolve the cycle by
486+
// using the value from that handler.
487+
fulfillReference(reference, cyclicHandler.value);
488+
resolveListeners.splice(i, 1);
489+
i--;
490+
if (rejectListeners !== null) {
491+
const rejectionIdx = rejectListeners.indexOf(reference);
492+
if (rejectionIdx !== -1) {
493+
rejectListeners.splice(rejectionIdx, 1);
494+
}
495+
}
496+
}
497+
}
498+
}
499+
// Fallthrough
500+
case PENDING:
450501
if (chunk.value) {
451502
for (let i = 0; i < resolveListeners.length; i++) {
452503
chunk.value.push(resolveListeners[i]);
@@ -1063,7 +1114,22 @@ function fulfillReference(
10631114
value = referencedChunk.value;
10641115
continue;
10651116
}
1066-
case BLOCKED:
1117+
case BLOCKED: {
1118+
// It is possible that we're blocked on our own chunk if it's a cycle.
1119+
// Before adding the listener to the inner chunk, let's check if it would
1120+
// result in a cycle.
1121+
const cyclicHandler = resolveBlockedCycle(
1122+
referencedChunk,
1123+
reference,
1124+
);
1125+
if (cyclicHandler !== null) {
1126+
// This reference points back to this chunk. We can resolve the cycle by
1127+
// using the value from that handler.
1128+
value = cyclicHandler.value;
1129+
continue;
1130+
}
1131+
// Fallthrough
1132+
}
10671133
case PENDING: {
10681134
// If we're not yet initialized we need to skip what we've already drilled
10691135
// through and then wait for the next value to become available.
@@ -1152,7 +1218,7 @@ function rejectReference(
11521218
reference: InitializationReference,
11531219
error: mixed,
11541220
): void {
1155-
const {response, handler} = reference;
1221+
const {handler} = reference;
11561222

11571223
if (handler.errored) {
11581224
// We've already errored. We could instead build up an AggregateError

0 commit comments

Comments
 (0)