@@ -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+
439466function 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