Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion packages/react-client/src/ReactFlightClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,11 @@ ReactPromise.prototype.then = function <T>(
initializeModuleChunk(chunk);
break;
}
if (__DEV__ && enableAsyncDebugInfo) {
if (
__DEV__ &&
enableAsyncDebugInfo &&
(typeof resolve !== 'function' || !(resolve: any).isReactInternalListener)
Copy link
Collaborator Author

@sebmarkbage sebmarkbage Jun 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The microtask here means that cycles don't resolve synchronously which means that console logs can miss them. This is not the only thing that can be async though. If you console log a client reference that hasn't loaded yet you get the same problem.

In the future, we should really make sure that the debug info can be parsed async while retaining console log order.

) {
// Because only native Promises get picked up when we're awaiting we need to wrap
// this in a native Promise in DEV. This means that these callbacks are no longer sync
// but the lazy initialization is still sync and the .value can be inspected after,
Expand Down Expand Up @@ -1052,6 +1056,10 @@ function waitForReference<T>(
}
}
}
// Use to avoid the microtask resolution in DEV.
if (__DEV__ && enableAsyncDebugInfo) {
(fulfill: any).isReactInternalListener = true;
}

function reject(error: mixed): void {
if (handler.errored) {
Expand Down
17 changes: 4 additions & 13 deletions packages/react-client/src/__tests__/ReactFlight-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3328,19 +3328,10 @@ describe('ReactFlight', () => {
await ReactNoopFlightClient.read(transport);

expect(mockConsoleLog).toHaveBeenCalledTimes(1);
// TODO: Support cyclic objects in console encoding.
// expect(mockConsoleLog.mock.calls[0][0]).toBe('hi');
// const cyclic2 = mockConsoleLog.mock.calls[0][1].cyclic;
// expect(cyclic2).not.toBe(cyclic); // Was serialized and therefore cloned
// expect(cyclic2.cycle).toBe(cyclic2);
expect(mockConsoleLog.mock.calls[0][0]).toBe(
'Unknown Value: React could not send it from the server.',
);
expect(mockConsoleLog.mock.calls[0][1].message).toBe(
'Converting circular structure to JSON\n' +
" --> starting at object with constructor 'Object'\n" +
" --- property 'cycle' closes the circle",
);
expect(mockConsoleLog.mock.calls[0][0]).toBe('hi');
const cyclic2 = mockConsoleLog.mock.calls[0][1].cyclic;
expect(cyclic2).not.toBe(cyclic); // Was serialized and therefore cloned
expect(cyclic2.cycle).toBe(cyclic2);
});

// @gate !__DEV__ || enableComponentPerformanceTrack
Expand Down
Loading
Loading