Skip to content

Commit 0378b46

Browse files
authored
[Flight] Include I/O not awaited in user space (#33715)
If I/O is not awaited in user space in a "previous" path we used to just drop it on the floor. There's a few strategies we could apply here. My first commit just emits it without an await but that would mean we don't have an await stack when there's no I/O in a follow up. I went with a strategy where the "previous" I/O is used only if the "next" didn't have I/O. This may still drop I/O on the floor if there's two back to back within internals for example. It would only log the first one even though the outer await may have started earlier. It may also log deeper in the "next" path if that had user space stacks and then the outer await will appear as if it awaited after. So it's not perfect.
1 parent bb40287 commit 0378b46

File tree

2 files changed

+393
-251
lines changed

2 files changed

+393
-251
lines changed

packages/react-server/src/ReactFlightServer.js

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2061,20 +2061,17 @@ function visitAsyncNode(
20612061
return null;
20622062
}
20632063
visited.add(node);
2064+
let previousIONode = null;
20642065
// First visit anything that blocked this sequence to start in the first place.
20652066
if (node.previous !== null && node.end > request.timeOrigin) {
2066-
// We ignore the returned io nodes here because if it wasn't awaited in user space,
2067-
// then we don't log it. It also means that it can just have been part of a previous
2068-
// component's render.
2069-
// TODO: This means that some I/O can get lost that was still blocking the sequence.
2070-
const ioNode = visitAsyncNode(
2067+
previousIONode = visitAsyncNode(
20712068
request,
20722069
task,
20732070
node.previous,
20742071
visited,
20752072
cutOff,
20762073
);
2077-
if (ioNode === undefined) {
2074+
if (previousIONode === undefined) {
20782075
// Undefined is used as a signal that we found a suitable aborted node and we don't have to find
20792076
// further aborted nodes.
20802077
return undefined;
@@ -2085,17 +2082,17 @@ function visitAsyncNode(
20852082
return node;
20862083
}
20872084
case UNRESOLVED_PROMISE_NODE: {
2088-
return null;
2085+
return previousIONode;
20892086
}
20902087
case PROMISE_NODE: {
20912088
if (node.end <= request.timeOrigin) {
20922089
// This was already resolved when we started this render. It must have been either something
20932090
// that's part of a start up sequence or externally cached data. We exclude that information.
20942091
// The technique for debugging the effects of uncached data on the render is to simply uncache it.
2095-
return null;
2092+
return previousIONode;
20962093
}
20972094
const awaited = node.awaited;
2098-
let match = null;
2095+
let match: void | null | PromiseNode | IONode = previousIONode;
20992096
if (awaited !== null) {
21002097
const ioNode = visitAsyncNode(request, task, awaited, visited, cutOff);
21012098
if (ioNode === undefined) {
@@ -2143,11 +2140,11 @@ function visitAsyncNode(
21432140
return match;
21442141
}
21452142
case UNRESOLVED_AWAIT_NODE: {
2146-
return null;
2143+
return previousIONode;
21472144
}
21482145
case AWAIT_NODE: {
21492146
const awaited = node.awaited;
2150-
let match = null;
2147+
let match: void | null | PromiseNode | IONode = previousIONode;
21512148
if (awaited !== null) {
21522149
const ioNode = visitAsyncNode(request, task, awaited, visited, cutOff);
21532150
if (ioNode === undefined) {

0 commit comments

Comments
 (0)