[Flight] Reduce risk of maximum call stack exceeded when emitting async sequence (#35159)

This commit is contained in:
Sebastian "Sebbie" Silbermann
2025-11-17 18:54:13 +01:00
committed by GitHub
parent fb2177c153
commit 45bc3c9f04

View File

@@ -2347,7 +2347,8 @@ function visitAsyncNodeImpl(
// The technique for debugging the effects of uncached data on the render is to simply uncache it.
return null;
}
let previousIONode = null;
let previousIONode: void | null | PromiseNode | IONode = null;
// First visit anything that blocked this sequence to start in the first place.
if (node.previous !== null) {
previousIONode = visitAsyncNode(
@@ -2363,12 +2364,20 @@ function visitAsyncNodeImpl(
return undefined;
}
}
// `found` represents the return value of the following switch statement.
// We can't use multiple `return` statements in the switch statement
// since that prevents Closure compiler from inlining `visitAsyncImpl`
// thus doubling the call stack size.
let found: void | null | PromiseNode | IONode;
switch (node.tag) {
case IO_NODE: {
return node;
found = node;
break;
}
case UNRESOLVED_PROMISE_NODE: {
return previousIONode;
found = previousIONode;
break;
}
case PROMISE_NODE: {
const awaited = node.awaited;
@@ -2379,7 +2388,8 @@ function visitAsyncNodeImpl(
if (ioNode === undefined) {
// Undefined is used as a signal that we found a suitable aborted node and we don't have to find
// further aborted nodes.
return undefined;
found = undefined;
break;
} else if (ioNode !== null) {
// This Promise was blocked on I/O. That's a signal that this Promise is interesting to log.
// We don't log it yet though. We return it to be logged by the point where it's awaited.
@@ -2436,10 +2446,12 @@ function visitAsyncNodeImpl(
forwardDebugInfo(request, task, debugInfo);
}
}
return match;
found = match;
break;
}
case UNRESOLVED_AWAIT_NODE: {
return previousIONode;
found = previousIONode;
break;
}
case AWAIT_NODE: {
const awaited = node.awaited;
@@ -2449,7 +2461,8 @@ function visitAsyncNodeImpl(
if (ioNode === undefined) {
// Undefined is used as a signal that we found a suitable aborted node and we don't have to find
// further aborted nodes.
return undefined;
found = undefined;
break;
} else if (ioNode !== null) {
const startTime: number = node.start;
const endTime: number = node.end;
@@ -2545,13 +2558,15 @@ function visitAsyncNodeImpl(
forwardDebugInfo(request, task, debugInfo);
}
}
return match;
found = match;
break;
}
default: {
// eslint-disable-next-line react-internal/prod-error-codes
throw new Error('Unknown AsyncSequence tag. This is a bug in React.');
}
}
return found;
}
function emitAsyncSequence(