Skip to content

Commit 7969827

Browse files
committed
Ensure blocked console log entries resolves in order
1 parent cb6a866 commit 7969827

File tree

1 file changed

+60
-32
lines changed

1 file changed

+60
-32
lines changed

packages/react-client/src/ReactFlightClient.js

Lines changed: 60 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,7 @@ export type Response = {
364364
_debugRootTask?: null | ConsoleTask, // DEV-only
365365
_debugFindSourceMapURL?: void | FindSourceMapURLCallback, // DEV-only
366366
_debugChannel?: void | DebugChannelCallback, // DEV-only
367+
_blockedConsole?: null | SomeChunk<ConsoleEntry>, // DEV-only
367368
_replayConsole: boolean, // DEV-only
368369
_rootEnvironmentName: string, // DEV-only, the requested environment name.
369370
};
@@ -2065,6 +2066,7 @@ function ResponseInstance(
20652066
}
20662067
this._debugFindSourceMapURL = findSourceMapURL;
20672068
this._debugChannel = debugChannel;
2069+
this._blockedConsole = null;
20682070
this._replayConsole = replayConsole;
20692071
this._rootEnvironmentName = rootEnv;
20702072
}
@@ -3134,12 +3136,14 @@ function getCurrentStackInDEV(): string {
31343136
const replayConsoleWithCallStack = {
31353137
'react-stack-bottom-frame': function (
31363138
response: Response,
3137-
methodName: string,
3138-
stackTrace: ReactStackTrace,
3139-
owner: null | ReactComponentInfo,
3140-
env: string,
3141-
args: Array<mixed>,
3139+
payload: ConsoleEntry,
31423140
): void {
3141+
const methodName = payload[0];
3142+
const stackTrace = payload[1];
3143+
const owner = payload[2];
3144+
const env = payload[3];
3145+
const args = payload.slice(4);
3146+
31433147
// There really shouldn't be anything else on the stack atm.
31443148
const prevStack = ReactSharedInternals.getCurrentStack;
31453149
ReactSharedInternals.getCurrentStack = getCurrentStackInDEV;
@@ -3177,21 +3181,25 @@ const replayConsoleWithCallStack = {
31773181

31783182
const replayConsoleWithCallStackInDEV: (
31793183
response: Response,
3180-
methodName: string,
3181-
stackTrace: ReactStackTrace,
3182-
owner: null | ReactComponentInfo,
3183-
env: string,
3184-
args: Array<mixed>,
3184+
payload: ConsoleEntry,
31853185
) => void = __DEV__
31863186
? // We use this technique to trick minifiers to preserve the function name.
31873187
(replayConsoleWithCallStack['react-stack-bottom-frame'].bind(
31883188
replayConsoleWithCallStack,
31893189
): any)
31903190
: (null: any);
31913191

3192+
type ConsoleEntry = [
3193+
string,
3194+
ReactStackTrace,
3195+
null | ReactComponentInfo,
3196+
string,
3197+
mixed,
3198+
];
3199+
31923200
function resolveConsoleEntry(
31933201
response: Response,
3194-
value: UninitializedModel,
3202+
json: UninitializedModel,
31953203
): void {
31963204
if (!__DEV__) {
31973205
// These errors should never make it into a build so we don't need to encode them in codes.json
@@ -3205,27 +3213,47 @@ function resolveConsoleEntry(
32053213
return;
32063214
}
32073215

3208-
const payload: [
3209-
string,
3210-
ReactStackTrace,
3211-
null | ReactComponentInfo,
3212-
string,
3213-
mixed,
3214-
] = parseModel(response, value);
3215-
const methodName = payload[0];
3216-
const stackTrace = payload[1];
3217-
const owner = payload[2];
3218-
const env = payload[3];
3219-
const args = payload.slice(4);
3220-
3221-
replayConsoleWithCallStackInDEV(
3222-
response,
3223-
methodName,
3224-
stackTrace,
3225-
owner,
3226-
env,
3227-
args,
3228-
);
3216+
const blockedChunk = response._blockedConsole;
3217+
if (blockedChunk == null) {
3218+
// If we're not blocked on any other chunks, we can try to eagerly initialize
3219+
// this as a fast-path to avoid awaiting them.
3220+
const chunk: ResolvedModelChunk<ConsoleEntry> = createResolvedModelChunk(
3221+
response,
3222+
json,
3223+
);
3224+
initializeModelChunk(chunk);
3225+
const initializedChunk: SomeChunk<ConsoleEntry> = chunk;
3226+
if (initializedChunk.status === INITIALIZED) {
3227+
replayConsoleWithCallStackInDEV(response, initializedChunk.value);
3228+
} else {
3229+
chunk.then(
3230+
v => replayConsoleWithCallStackInDEV(response, v),
3231+
e => {
3232+
// Ignore console errors for now. Unnecessary noise.
3233+
},
3234+
);
3235+
response._blockedConsole = chunk;
3236+
}
3237+
} else {
3238+
// We're still waiting on a previous chunk so we can't enqueue quite yet.
3239+
const chunk: SomeChunk<ConsoleEntry> = createPendingChunk(response);
3240+
chunk.then(
3241+
v => replayConsoleWithCallStackInDEV(response, v),
3242+
e => {
3243+
// Ignore console errors for now. Unnecessary noise.
3244+
},
3245+
);
3246+
response._blockedConsole = chunk;
3247+
const unblock = () => {
3248+
if (response._blockedConsole === chunk) {
3249+
// We were still the last chunk so we can now clear the queue and return
3250+
// to synchronous emitting.
3251+
response._blockedConsole = null;
3252+
}
3253+
resolveModelChunk(response, chunk, json);
3254+
};
3255+
blockedChunk.then(unblock, unblock);
3256+
}
32293257
}
32303258

32313259
function initializeIOInfo(response: Response, ioInfo: ReactIOInfo): void {

0 commit comments

Comments
 (0)