Skip to content

Commit 9dbe4bd

Browse files
committed
[Flight] Reduce risk of maximum call stack exceeded when tracking async debug info
1 parent 8c15edd commit 9dbe4bd

File tree

1 file changed

+22
-8
lines changed

1 file changed

+22
-8
lines changed

packages/react-server/src/ReactFlightServer.js

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2339,7 +2339,8 @@ function visitAsyncNodeImpl(
23392339
// The technique for debugging the effects of uncached data on the render is to simply uncache it.
23402340
return null;
23412341
}
2342-
let previousIONode = null;
2342+
2343+
let previousIONode: void | null | PromiseNode | IONode = null;
23432344
// First visit anything that blocked this sequence to start in the first place.
23442345
if (node.previous !== null) {
23452346
previousIONode = visitAsyncNode(
@@ -2355,12 +2356,19 @@ function visitAsyncNodeImpl(
23552356
return undefined;
23562357
}
23572358
}
2359+
2360+
// `found` represents the return value of the following switch statement.
2361+
// We can't use `return` statements since that will not result in `visitAsyncImpl`
2362+
// to be inlined by Closure compiler thus doubling the stack depth.
2363+
let found: void | null | PromiseNode | IONode;
23582364
switch (node.tag) {
23592365
case IO_NODE: {
2360-
return node;
2366+
found = node;
2367+
break;
23612368
}
23622369
case UNRESOLVED_PROMISE_NODE: {
2363-
return previousIONode;
2370+
found = previousIONode;
2371+
break;
23642372
}
23652373
case PROMISE_NODE: {
23662374
const awaited = node.awaited;
@@ -2371,7 +2379,8 @@ function visitAsyncNodeImpl(
23712379
if (ioNode === undefined) {
23722380
// Undefined is used as a signal that we found a suitable aborted node and we don't have to find
23732381
// further aborted nodes.
2374-
return undefined;
2382+
found = undefined;
2383+
break;
23752384
} else if (ioNode !== null) {
23762385
// This Promise was blocked on I/O. That's a signal that this Promise is interesting to log.
23772386
// We don't log it yet though. We return it to be logged by the point where it's awaited.
@@ -2428,10 +2437,12 @@ function visitAsyncNodeImpl(
24282437
forwardDebugInfo(request, task, debugInfo);
24292438
}
24302439
}
2431-
return match;
2440+
found = match;
2441+
break;
24322442
}
24332443
case UNRESOLVED_AWAIT_NODE: {
2434-
return previousIONode;
2444+
found = previousIONode;
2445+
break;
24352446
}
24362447
case AWAIT_NODE: {
24372448
const awaited = node.awaited;
@@ -2441,7 +2452,8 @@ function visitAsyncNodeImpl(
24412452
if (ioNode === undefined) {
24422453
// Undefined is used as a signal that we found a suitable aborted node and we don't have to find
24432454
// further aborted nodes.
2444-
return undefined;
2455+
found = undefined;
2456+
break;
24452457
} else if (ioNode !== null) {
24462458
const startTime: number = node.start;
24472459
const endTime: number = node.end;
@@ -2537,13 +2549,15 @@ function visitAsyncNodeImpl(
25372549
forwardDebugInfo(request, task, debugInfo);
25382550
}
25392551
}
2540-
return match;
2552+
found = match;
2553+
break;
25412554
}
25422555
default: {
25432556
// eslint-disable-next-line react-internal/prod-error-codes
25442557
throw new Error('Unknown AsyncSequence tag. This is a bug in React.');
25452558
}
25462559
}
2560+
return found;
25472561
}
25482562

25492563
function emitAsyncSequence(

0 commit comments

Comments
 (0)