Skip to content

Commit 561ee24

Browse files
authored
[Fizz] Push halted await to the owner stack for late-arriving I/O info (facebook#35019)
1 parent 488d88b commit 561ee24

File tree

14 files changed

+363
-40
lines changed

14 files changed

+363
-40
lines changed

packages/react-client/src/ReactFlightClient.js

Lines changed: 79 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,7 @@ type Response = {
367367
_debugRootStack?: null | Error, // DEV-only
368368
_debugRootTask?: null | ConsoleTask, // DEV-only
369369
_debugStartTime: number, // DEV-only
370+
_debugEndTime?: number, // DEV-only
370371
_debugIOStarted: boolean, // DEV-only
371372
_debugFindSourceMapURL?: void | FindSourceMapURLCallback, // DEV-only
372373
_debugChannel?: void | DebugChannel, // DEV-only
@@ -500,6 +501,34 @@ function createErrorChunk<T>(
500501
return new ReactPromise(ERRORED, null, error);
501502
}
502503

504+
function filterDebugInfo(
505+
response: Response,
506+
value: {_debugInfo: ReactDebugInfo, ...},
507+
) {
508+
if (response._debugEndTime === null) {
509+
// No end time was defined, so we keep all debug info entries.
510+
return;
511+
}
512+
513+
// Remove any debug info entries that arrived after the defined end time.
514+
const relativeEndTime =
515+
response._debugEndTime -
516+
// $FlowFixMe[prop-missing]
517+
performance.timeOrigin;
518+
const debugInfo = [];
519+
for (let i = 0; i < value._debugInfo.length; i++) {
520+
const info = value._debugInfo[i];
521+
if (typeof info.time === 'number' && info.time > relativeEndTime) {
522+
break;
523+
}
524+
if (info.awaited != null && info.awaited.end > relativeEndTime) {
525+
break;
526+
}
527+
debugInfo.push(info);
528+
}
529+
value._debugInfo = debugInfo;
530+
}
531+
503532
function moveDebugInfoFromChunkToInnerValue<T>(
504533
chunk: InitializedChunk<T> | InitializedStreamChunk<any>,
505534
value: T,
@@ -534,7 +563,17 @@ function moveDebugInfoFromChunkToInnerValue<T>(
534563
}
535564
}
536565

566+
function processChunkDebugInfo<T>(
567+
response: Response,
568+
chunk: InitializedChunk<T> | InitializedStreamChunk<any>,
569+
value: T,
570+
): void {
571+
filterDebugInfo(response, chunk);
572+
moveDebugInfoFromChunkToInnerValue(chunk, value);
573+
}
574+
537575
function wakeChunk<T>(
576+
response: Response,
538577
listeners: Array<InitializationReference | (T => mixed)>,
539578
value: T,
540579
chunk: InitializedChunk<T>,
@@ -544,16 +583,17 @@ function wakeChunk<T>(
544583
if (typeof listener === 'function') {
545584
listener(value);
546585
} else {
547-
fulfillReference(listener, value, chunk);
586+
fulfillReference(response, listener, value, chunk);
548587
}
549588
}
550589

551590
if (__DEV__) {
552-
moveDebugInfoFromChunkToInnerValue(chunk, value);
591+
processChunkDebugInfo(response, chunk, value);
553592
}
554593
}
555594

556595
function rejectChunk(
596+
response: Response,
557597
listeners: Array<InitializationReference | (mixed => mixed)>,
558598
error: mixed,
559599
): void {
@@ -562,7 +602,7 @@ function rejectChunk(
562602
if (typeof listener === 'function') {
563603
listener(error);
564604
} else {
565-
rejectReference(listener, error);
605+
rejectReference(response, listener.handler, error);
566606
}
567607
}
568608
}
@@ -595,13 +635,14 @@ function resolveBlockedCycle<T>(
595635
}
596636

597637
function wakeChunkIfInitialized<T>(
638+
response: Response,
598639
chunk: SomeChunk<T>,
599640
resolveListeners: Array<InitializationReference | (T => mixed)>,
600641
rejectListeners: null | Array<InitializationReference | (mixed => mixed)>,
601642
): void {
602643
switch (chunk.status) {
603644
case INITIALIZED:
604-
wakeChunk(resolveListeners, chunk.value, chunk);
645+
wakeChunk(response, resolveListeners, chunk.value, chunk);
605646
break;
606647
case BLOCKED:
607648
// It is possible that we're blocked on our own chunk if it's a cycle.
@@ -615,7 +656,7 @@ function wakeChunkIfInitialized<T>(
615656
if (cyclicHandler !== null) {
616657
// This reference points back to this chunk. We can resolve the cycle by
617658
// using the value from that handler.
618-
fulfillReference(reference, cyclicHandler.value, chunk);
659+
fulfillReference(response, reference, cyclicHandler.value, chunk);
619660
resolveListeners.splice(i, 1);
620661
i--;
621662
if (rejectListeners !== null) {
@@ -629,14 +670,15 @@ function wakeChunkIfInitialized<T>(
629670
case INITIALIZED:
630671
const initializedChunk: InitializedChunk<T> = (chunk: any);
631672
wakeChunk(
673+
response,
632674
resolveListeners,
633675
initializedChunk.value,
634676
initializedChunk,
635677
);
636678
return;
637679
case ERRORED:
638680
if (rejectListeners !== null) {
639-
rejectChunk(rejectListeners, chunk.reason);
681+
rejectChunk(response, rejectListeners, chunk.reason);
640682
}
641683
return;
642684
}
@@ -666,7 +708,7 @@ function wakeChunkIfInitialized<T>(
666708
break;
667709
case ERRORED:
668710
if (rejectListeners) {
669-
rejectChunk(rejectListeners, chunk.reason);
711+
rejectChunk(response, rejectListeners, chunk.reason);
670712
}
671713
break;
672714
}
@@ -724,7 +766,7 @@ function triggerErrorOnChunk<T>(
724766
erroredChunk.status = ERRORED;
725767
erroredChunk.reason = error;
726768
if (listeners !== null) {
727-
rejectChunk(listeners, error);
769+
rejectChunk(response, listeners, error);
728770
}
729771
}
730772

@@ -832,7 +874,7 @@ function resolveModelChunk<T>(
832874
// longer be rendered or might not be the highest pri.
833875
initializeModelChunk(resolvedChunk);
834876
// The status might have changed after initialization.
835-
wakeChunkIfInitialized(chunk, resolveListeners, rejectListeners);
877+
wakeChunkIfInitialized(response, chunk, resolveListeners, rejectListeners);
836878
}
837879
}
838880

@@ -861,12 +903,11 @@ function resolveModuleChunk<T>(
861903
}
862904
if (resolveListeners !== null) {
863905
initializeModuleChunk(resolvedChunk);
864-
wakeChunkIfInitialized(chunk, resolveListeners, rejectListeners);
906+
wakeChunkIfInitialized(response, chunk, resolveListeners, rejectListeners);
865907
}
866908
}
867909

868910
type InitializationReference = {
869-
response: Response, // TODO: Remove Response from here and pass it through instead.
870911
handler: InitializationHandler,
871912
parentObject: Object,
872913
key: string,
@@ -1005,7 +1046,7 @@ function initializeModelChunk<T>(chunk: ResolvedModelChunk<T>): void {
10051046
if (typeof listener === 'function') {
10061047
listener(value);
10071048
} else {
1008-
fulfillReference(listener, value, cyclicChunk);
1049+
fulfillReference(response, listener, value, cyclicChunk);
10091050
}
10101051
}
10111052
}
@@ -1026,7 +1067,7 @@ function initializeModelChunk<T>(chunk: ResolvedModelChunk<T>): void {
10261067
initializedChunk.value = value;
10271068

10281069
if (__DEV__) {
1029-
moveDebugInfoFromChunkToInnerValue(initializedChunk, value);
1070+
processChunkDebugInfo(response, initializedChunk, value);
10301071
}
10311072
} catch (error) {
10321073
const erroredChunk: ErroredChunk<T> = (chunk: any);
@@ -1413,11 +1454,12 @@ function getChunk(response: Response, id: number): SomeChunk<any> {
14131454
}
14141455

14151456
function fulfillReference(
1457+
response: Response,
14161458
reference: InitializationReference,
14171459
value: any,
14181460
fulfilledChunk: SomeChunk<any>,
14191461
): void {
1420-
const {response, handler, parentObject, key, map, path} = reference;
1462+
const {handler, parentObject, key, map, path} = reference;
14211463

14221464
for (let i = 1; i < path.length; i++) {
14231465
while (
@@ -1487,7 +1529,11 @@ function fulfillReference(
14871529
return;
14881530
}
14891531
default: {
1490-
rejectReference(reference, referencedChunk.reason);
1532+
rejectReference(
1533+
response,
1534+
reference.handler,
1535+
referencedChunk.reason,
1536+
);
14911537
return;
14921538
}
14931539
}
@@ -1585,21 +1631,20 @@ function fulfillReference(
15851631
initializedChunk.value = handler.value;
15861632
initializedChunk.reason = handler.reason; // Used by streaming chunks
15871633
if (resolveListeners !== null) {
1588-
wakeChunk(resolveListeners, handler.value, initializedChunk);
1634+
wakeChunk(response, resolveListeners, handler.value, initializedChunk);
15891635
} else {
15901636
if (__DEV__) {
1591-
moveDebugInfoFromChunkToInnerValue(initializedChunk, handler.value);
1637+
processChunkDebugInfo(response, initializedChunk, handler.value);
15921638
}
15931639
}
15941640
}
15951641
}
15961642

15971643
function rejectReference(
1598-
reference: InitializationReference,
1644+
response: Response,
1645+
handler: InitializationHandler,
15991646
error: mixed,
16001647
): void {
1601-
const {handler, response} = reference;
1602-
16031648
if (handler.errored) {
16041649
// We've already errored. We could instead build up an AggregateError
16051650
// but if there are multiple errors we just take the first one like
@@ -1690,7 +1735,6 @@ function waitForReference<T>(
16901735
}
16911736

16921737
const reference: InitializationReference = {
1693-
response,
16941738
handler,
16951739
parentObject,
16961740
key,
@@ -1838,10 +1882,10 @@ function loadServerReference<A: Iterable<any>, T>(
18381882
initializedChunk.status = INITIALIZED;
18391883
initializedChunk.value = handler.value;
18401884
if (resolveListeners !== null) {
1841-
wakeChunk(resolveListeners, handler.value, initializedChunk);
1885+
wakeChunk(response, resolveListeners, handler.value, initializedChunk);
18421886
} else {
18431887
if (__DEV__) {
1844-
moveDebugInfoFromChunkToInnerValue(initializedChunk, handler.value);
1888+
processChunkDebugInfo(response, initializedChunk, handler.value);
18451889
}
18461890
}
18471891
}
@@ -2578,6 +2622,7 @@ function ResponseInstance(
25782622
replayConsole: boolean, // DEV-only
25792623
environmentName: void | string, // DEV-only
25802624
debugStartTime: void | number, // DEV-only
2625+
debugEndTime: void | number, // DEV-only
25812626
debugChannel: void | DebugChannel, // DEV-only
25822627
) {
25832628
const chunks: Map<number, SomeChunk<any>> = new Map();
@@ -2645,6 +2690,7 @@ function ResponseInstance(
26452690
// and is not considered I/O required to load the stream.
26462691
setTimeout(markIOStarted.bind(this), 0);
26472692
}
2693+
this._debugEndTime = debugEndTime == null ? null : debugEndTime;
26482694
this._debugFindSourceMapURL = findSourceMapURL;
26492695
this._debugChannel = debugChannel;
26502696
this._blockedConsole = null;
@@ -2688,6 +2734,7 @@ export function createResponse(
26882734
replayConsole: boolean, // DEV-only
26892735
environmentName: void | string, // DEV-only
26902736
debugStartTime: void | number, // DEV-only
2737+
debugEndTime: void | number, // DEV-only
26912738
debugChannel: void | DebugChannel, // DEV-only
26922739
): WeakResponse {
26932740
return getWeakResponse(
@@ -2704,6 +2751,7 @@ export function createResponse(
27042751
replayConsole,
27052752
environmentName,
27062753
debugStartTime,
2754+
debugEndTime,
27072755
debugChannel,
27082756
),
27092757
);
@@ -3075,10 +3123,10 @@ function resolveStream<T: ReadableStream | $AsyncIterable<any, any, void>>(
30753123
resolvedChunk.value = stream;
30763124
resolvedChunk.reason = controller;
30773125
if (resolveListeners !== null) {
3078-
wakeChunk(resolveListeners, chunk.value, (chunk: any));
3126+
wakeChunk(response, resolveListeners, chunk.value, (chunk: any));
30793127
} else {
30803128
if (__DEV__) {
3081-
moveDebugInfoFromChunkToInnerValue(resolvedChunk, stream);
3129+
processChunkDebugInfo(response, resolvedChunk, stream);
30823130
}
30833131
}
30843132
}
@@ -3218,7 +3266,12 @@ function startAsyncIterable<T>(
32183266
initializedChunk.status = INITIALIZED;
32193267
initializedChunk.value = {done: false, value: value};
32203268
if (resolveListeners !== null) {
3221-
wakeChunkIfInitialized(chunk, resolveListeners, rejectListeners);
3269+
wakeChunkIfInitialized(
3270+
response,
3271+
chunk,
3272+
resolveListeners,
3273+
rejectListeners,
3274+
);
32223275
}
32233276
}
32243277
nextWriteIndex++;

packages/react-server-dom-esm/src/client/ReactFlightDOMClientBrowser.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export type Options = {
5353
replayConsoleLogs?: boolean,
5454
environmentName?: string,
5555
startTime?: number,
56+
endTime?: number,
5657
};
5758

5859
function createDebugCallbackFromWritableStream(
@@ -107,6 +108,7 @@ function createResponseFromOptions(options: void | Options) {
107108
__DEV__ && options && options.startTime != null
108109
? options.startTime
109110
: undefined,
111+
__DEV__ && options && options.endTime != null ? options.endTime : undefined,
110112
debugChannel,
111113
);
112114
}

packages/react-server-dom-esm/src/client/ReactFlightDOMClientNode.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export type Options = {
5858
replayConsoleLogs?: boolean,
5959
environmentName?: string,
6060
startTime?: number,
61+
endTime?: number,
6162
// For the Node.js client we only support a single-direction debug channel.
6263
debugChannel?: Readable,
6364
};
@@ -116,6 +117,7 @@ function createFromNodeStream<T>(
116117
__DEV__ && options && options.startTime != null
117118
? options.startTime
118119
: undefined,
120+
__DEV__ && options && options.endTime != null ? options.endTime : undefined,
119121
debugChannel,
120122
);
121123

packages/react-server-dom-parcel/src/client/ReactFlightDOMClientBrowser.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ function createResponseFromOptions(options: void | Options) {
132132
__DEV__ && options && options.startTime != null
133133
? options.startTime
134134
: undefined,
135+
__DEV__ && options && options.endTime != null ? options.endTime : undefined,
135136
debugChannel,
136137
);
137138
}
@@ -209,6 +210,7 @@ export type Options = {
209210
replayConsoleLogs?: boolean,
210211
environmentName?: string,
211212
startTime?: number,
213+
endTime?: number,
212214
};
213215

214216
export function createFromReadableStream<T>(

packages/react-server-dom-parcel/src/client/ReactFlightDOMClientEdge.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export type Options = {
8080
replayConsoleLogs?: boolean,
8181
environmentName?: string,
8282
startTime?: number,
83+
endTime?: number,
8384
// For the Edge client we only support a single-direction debug channel.
8485
debugChannel?: {readable?: ReadableStream, ...},
8586
};
@@ -111,6 +112,7 @@ function createResponseFromOptions(options?: Options) {
111112
__DEV__ && options && options.startTime != null
112113
? options.startTime
113114
: undefined,
115+
__DEV__ && options && options.endTime != null ? options.endTime : undefined,
114116
debugChannel,
115117
);
116118
}

0 commit comments

Comments
 (0)