77 * @flow
88 */
99
10- import type { ReactStackTrace } from 'shared/ReactTypes' ;
10+ import type { ReactStackTrace , ReactComponentInfo } from 'shared/ReactTypes' ;
1111
1212import type {
1313 AsyncSequence ,
@@ -40,6 +40,28 @@ const pendingOperations: Map<number, AsyncSequence> =
4040// Keep the last resolved await as a workaround for async functions missing data.
4141let lastRanAwait : null | AwaitNode = null ;
4242
43+ // These two maps work together to track what async functions are blocked on when aborting:
44+ //
45+ // 1. unresolvedPromiseNodesByOwner: Maps owner -> Promise (to find the Promise to link)
46+ // When a Promise is created, we track it by its owner. This typically captures async
47+ // function return Promises. Sync components may also have Promises tracked here, but
48+ // they won't be linked since sync functions can't have awaits with matching owners.
49+ //
50+ // 2. internalAwaitNodesByPromise: Maps Promise -> await (stores the actual link)
51+ // When an await happens with the same owner as a tracked Promise, we link that Promise
52+ // to the await. This shows what the async function is currently blocked on.
53+ //
54+ // By storing the links separately from the regular awaited field, we can use this information
55+ // only during abort scenarios without affecting normal rendering.
56+ const unresolvedPromiseNodesByOwner : WeakMap <
57+ ReactComponentInfo ,
58+ UnresolvedPromiseNode ,
59+ > = new WeakMap ( ) ;
60+ const internalAwaitNodesByPromise : WeakMap <
61+ UnresolvedPromiseNode | PromiseNode ,
62+ UnresolvedAwaitNode | AwaitNode ,
63+ > = new WeakMap ( ) ;
64+
4365function resolvePromiseOrAwaitNode (
4466 unresolvedNode : UnresolvedAwaitNode | UnresolvedPromiseNode ,
4567 endTime : number ,
@@ -114,16 +136,27 @@ export function initAsyncDebugInfo(): void {
114136 }
115137 }
116138 const current = pendingOperations . get ( currentAsyncId ) ;
139+ const owner = resolveOwner ( ) ;
117140 node = ( {
118141 tag : UNRESOLVED_AWAIT_NODE ,
119- owner : resolveOwner ( ) ,
142+ owner : owner ,
120143 stack : stack ,
121144 start : performance . now ( ) ,
122145 end : - 1.1 , // set when resolved.
123146 promise : promiseRef ,
124147 awaited : trigger , // The thing we're awaiting on. Might get overrriden when we resolve.
125148 previous : current === undefined ? null : current , // The path that led us here.
126149 } : UnresolvedAwaitNode ) ;
150+ // Link the owner's Promise to this await so we can track what it's blocked on.
151+ // This only links when the await and Promise have the same owner (i.e., async functions
152+ // awaiting within themselves). Promises from sync components won't match any awaits.
153+ // We store this in a separate WeakMap to avoid affecting normal rendering.
154+ if ( owner !== null ) {
155+ const ownerPromiseNode = unresolvedPromiseNodesByOwner . get ( owner ) ;
156+ if ( ownerPromiseNode !== undefined ) {
157+ internalAwaitNodesByPromise . set ( ownerPromiseNode , node ) ;
158+ }
159+ }
127160 } else {
128161 const owner = resolveOwner ( ) ;
129162 node = ( {
@@ -140,6 +173,17 @@ export function initAsyncDebugInfo(): void {
140173 : trigger ,
141174 previous : null ,
142175 } : UnresolvedPromiseNode ) ;
176+ // Track Promises by owner so awaits with matching owners can link to them.
177+ // Only track the first Promise per owner. This typically captures async function
178+ // return Promises, but may also track Promises from sync components - those won't
179+ // be linked since sync functions can't have awaits with matching owners.
180+ if (
181+ owner !== null &&
182+ trigger === undefined &&
183+ ! unresolvedPromiseNodesByOwner . has ( owner )
184+ ) {
185+ unresolvedPromiseNodesByOwner . set ( owner , node ) ;
186+ }
143187 }
144188 } else if (
145189 type !== 'Microtask ' &&
@@ -356,3 +400,10 @@ export function getAsyncSequenceFromPromise(
356400 }
357401 return node ;
358402}
403+
404+ export function getInternalAwaitNode (
405+ promiseNode : UnresolvedPromiseNode | PromiseNode ,
406+ ) : null | UnresolvedAwaitNode | AwaitNode {
407+ const awaitNode = internalAwaitNodesByPromise . get ( promiseNode ) ;
408+ return awaitNode === undefined ? null : awaitNode ;
409+ }
0 commit comments