@@ -75,6 +75,7 @@ import type {
7575 AsyncSequence ,
7676 IONode ,
7777 PromiseNode ,
78+ UnresolvedAwaitNode ,
7879 UnresolvedPromiseNode ,
7980} from './ReactFlightAsyncSequence' ;
8081
@@ -95,6 +96,7 @@ import {
9596 markAsyncSequenceRootTask ,
9697 getCurrentAsyncSequence ,
9798 getAsyncSequenceFromPromise ,
99+ getInternalAwaitNode ,
98100 parseStackTrace ,
99101 parseStackTracePrivate ,
100102 supportsComponentStorage ,
@@ -2305,7 +2307,18 @@ function visitAsyncNode(
23052307 // We aborted this render. If this Promise spanned the abort time it was probably the
23062308 // Promise that was aborted. This won't necessarily have I/O associated with it but
23072309 // it's a point of interest.
2308- match = node ;
2310+ // However, if the Promise and IO node have the same owner, it likely means a sync component
2311+ // created both the Promise and initiated the I/O. Prefer the IO node for more specific info.
2312+ if (
2313+ ioNode !== null &&
2314+ ioNode . tag === IO_NODE &&
2315+ node . owner !== null &&
2316+ ioNode . owner === node . owner
2317+ ) {
2318+ match = ioNode ;
2319+ } else {
2320+ match = node ;
2321+ }
23092322 }
23102323 } else if ( ioNode !== null ) {
23112324 // This Promise was blocked on I/O. That's a signal that this Promise is interesting to log.
@@ -4417,7 +4430,7 @@ function outlineIOInfo(request: Request, ioInfo: ReactIOInfo): void {
44174430
44184431function serializeIONode (
44194432 request : Request ,
4420- ioNode : IONode | PromiseNode | UnresolvedPromiseNode ,
4433+ ioNode : IONode | PromiseNode | UnresolvedPromiseNode | UnresolvedAwaitNode ,
44214434 promiseRef : null | WeakRef < Promise < mixed >> ,
44224435) : string {
44234436 const existingRef = request . writtenDebugObjects . get ( ioNode ) ;
@@ -5383,26 +5396,29 @@ function forwardDebugInfoFromAbortedTask(request: Request, task: Task): void {
53835396 // See if any of the dependencies are resolved yet.
53845397 node = node . awaited ;
53855398 }
5399+ // For unresolved Promises, check if we have an internal await node that shows what
5400+ // the async function is currently blocked on. For resolved Promises, the regular
5401+ // awaited field already contains the necessary information.
53865402 if ( node . tag === UNRESOLVED_PROMISE_NODE ) {
5387- // We don't know what Promise will eventually end up resolving this Promise and if it
5388- // was I/O at all. However, we assume that it was some kind of I/O since it didn't
5389- // complete in time before aborting.
5390- // The best we can do is try to emit the stack of where this Promise was created.
5403+ const internalAwait = getInternalAwaitNode ( node ) ;
5404+ if ( internalAwait !== null ) {
5405+ node = internalAwait ;
5406+ }
5407+ }
5408+ if ( node . tag === UNRESOLVED_AWAIT_NODE ) {
5409+ // We found the await that's blocking. Use its stack to show where the component is stuck.
53915410 serializeIONode ( request , node , null ) ;
53925411 request . pendingChunks ++ ;
53935412 const env = ( 0 , request . environmentName ) ( ) ;
53945413 const asyncInfo : ReactAsyncInfo = {
5395- awaited : ( ( node : any ) : ReactIOInfo ) , // This is deduped by this reference.
5414+ awaited : ( ( node : any ) : ReactIOInfo ) ,
53965415 env : env ,
53975416 } ;
5398- // We don't have a start time for this await but in case there was no start time emitted
5399- // we need to include something. TODO: We should maybe ideally track the time when we
5400- // called .then() but without updating the task.time field since that's used for the cutoff.
54015417 advanceTaskTime ( request , task , task . time ) ;
54025418 emitDebugChunk ( request , task . id , asyncInfo ) ;
54035419 } else {
5404- // We have a resolved Promise. Its debug info can include both awaited data and rejected
5405- // promises after the abort.
5420+ // We have a resolved or unresolved Promise. Its debug info can include both awaited
5421+ // data and rejected promises after the abort.
54065422 emitAsyncSequence ( request , task , sequence , debugInfo , null , null ) ;
54075423 }
54085424 }
0 commit comments