2316
request: Request,
2317
task: Task,
2318
node: AsyncSequence,
2319
- visited: Set<AsyncSequence | ReactDebugInfo>,
2319
+ visited: Map<
2320
+ AsyncSequence | ReactDebugInfo,
2321
+ void | null | PromiseNode | IONode,
2322
+ >,
2323
cutOff: number,
2324
): void | null | PromiseNode | IONode {
2325
if (visited.has(node)) {
2326
// It's possible to visit them same node twice when it's part of both an "awaited" path
2327
// and a "previous" path. This also gracefully handles cycles which would be a bug.
2325
- return null;
2328
+ return visited.get(node);
2329
+ }
2330
+ // Set it as visited early in case we see ourselves before returning.
2331
+ visited.set(node, null);
2332
+ const result = visitAsyncNodeImpl(request, task, node, visited, cutOff);
2333
+ if (result !== null) {
2334
+ // If we ended up with a value, let's use that value for future visits.
2335
+ visited.set(node, result);
2336
}
2327
- visited.add(node);
2337
+ return result;
2338
+}
2339
+
2340
+function visitAsyncNodeImpl(
2341
+ request: Request,
2342
+ task: Task,
2343
+ node: AsyncSequence,
2344
+ visited: Map<
2345
+ AsyncSequence | ReactDebugInfo,
2346
+ void | null | PromiseNode | IONode,
2347
+ >,
2348
+ cutOff: number,
2349
+): void | null | PromiseNode | IONode {
2350
if (node.end >= 0 && node.end <= request.timeOrigin) {
2351
// This was already resolved when we started this render. It must have been either something
2352
// that's part of a start up sequence or externally cached data. We exclude that information.
2438
if (promise !== undefined) {
2439
const debugInfo = promise._debugInfo;
2440
if (debugInfo != null && !visited.has(debugInfo)) {
2419
- visited.add(debugInfo);
2441
+ visited.set(debugInfo, null);
2442
forwardDebugInfo(request, task, debugInfo);
2443
}
2444
}
2505
// Promise that was ultimately awaited by the user space await.
2506
serializeIONode(request, ioNode, awaited.promise);
2507
2508
+ // If we ever visit this I/O node again, skip it because we already emitted this
2509
+ // exact entry and we don't need two awaits on the same thing.
2510
+ visited.set(ioNode, null);
2511
+
2512
// Ensure the owner is already outlined.
2513
if (node.owner != null) {
2514
outlineComponentInfo(request, node.owner);
2547
if (promise !== undefined) {
2548
const debugInfo = promise._debugInfo;
2549
if (debugInfo != null && !visited.has(debugInfo)) {
2524
- visited.add(debugInfo);
2550
+ visited.set(debugInfo, null);
2551
forwardDebugInfo(request, task, debugInfo);
2552
}
2553
}
2568
owner: null | ReactComponentInfo,
2569
stack: null | Error,
2570
): void {
2545
- const visited: Set<AsyncSequence | ReactDebugInfo> = new Set();
2571
+ const visited: Map<
2572
+ AsyncSequence | ReactDebugInfo,
2573
+ void | null | PromiseNode | IONode,
2574
+ > = new Map();
2575
if (__DEV__ && alreadyForwardedDebugInfo) {
2547
- visited.add(alreadyForwardedDebugInfo);
2576
+ visited.set(alreadyForwardedDebugInfo, null);
2577
}
2578
const awaitedNode = visitAsyncNode(request, task, node, visited, task.time);
2579
if (awaitedNode === undefined) {