@@ -30,6 +30,9 @@ import {enableAsyncDebugInfo} from 'shared/ReactFeatureFlags';
30
30
const pendingOperations : Map < number , AsyncSequence > =
31
31
__DEV__ && enableAsyncDebugInfo ? new Map ( ) : ( null : any ) ;
32
32
33
+ // Keep the last resolved await as a workaround for async functions missing data.
34
+ let lastRanAwait : null | AwaitNode = null ;
35
+
33
36
function resolvePromiseOrAwaitNode (
34
37
unresolvedNode : UnresolvedAwaitNode | UnresolvedPromiseNode ,
35
38
endTime : number ,
@@ -152,23 +155,49 @@ export function initAsyncDebugInfo(): void {
152
155
if ( node !== undefined ) {
153
156
switch ( node . tag ) {
154
157
case IO_NODE : {
158
+ lastRanAwait = null ;
155
159
// Log the end time when we resolved the I/O. This can happen
156
160
// more than once if it's a recurring resource like a connection.
157
161
const ioNode : IONode = ( node : any ) ;
158
162
ioNode . end = performance . now ( ) ;
159
163
break ;
160
164
}
161
- case UNRESOLVED_AWAIT_NODE :
162
- case UNRESOLVED_PROMISE_NODE : {
165
+ case UNRESOLVED_AWAIT_NODE : {
163
166
// If we begin before we resolve, that means that this is actually already resolved but
164
167
// the promiseResolve hook is called at the end of the execution. So we track the time
165
- // in the beginning instead.
166
- resolvePromiseOrAwaitNode ( node , performance . now ( ) ) ;
168
+ // in the before call instead.
169
+ // $FlowFixMe
170
+ lastRanAwait = resolvePromiseOrAwaitNode ( node , performance . now ( ) ) ;
167
171
break ;
168
172
}
173
+ case AWAIT_NODE : {
174
+ lastRanAwait = node ;
175
+ break ;
176
+ }
177
+ case UNRESOLVED_PROMISE_NODE : {
178
+ // We typically don't expected Promises to have an execution scope since only the awaits
179
+ // have a then() callback. However, this can happen for native async functions. The last
180
+ // piece of code that executes the return after the last await has the execution context
181
+ // of the Promise.
182
+ const resolvedNode = resolvePromiseOrAwaitNode (
183
+ node ,
184
+ performance . now ( ) ,
185
+ ) ;
186
+ // We are missing information about what this was unblocked by but we can guess that it
187
+ // was whatever await we ran last since this will continue in a microtask after that.
188
+ // This is not perfect because there could potentially be other microtasks getting in
189
+ // between.
190
+ resolvedNode . previous = lastRanAwait ;
191
+ lastRanAwait = null ;
192
+ break ;
193
+ }
194
+ default : {
195
+ lastRanAwait = null ;
196
+ }
169
197
}
170
198
}
171
199
} ,
200
+
172
201
promiseResolve ( asyncId : number ) : void {
173
202
const node = pendingOperations . get ( asyncId ) ;
174
203
if ( node !== undefined ) {
@@ -181,8 +210,8 @@ export function initAsyncDebugInfo(): void {
181
210
}
182
211
case AWAIT_NODE :
183
212
case PROMISE_NODE : {
213
+ // We already resolved this in the begin hook.
184
214
resolvedNode = node ;
185
- // We already resolved this in the begin phase.
186
215
break ;
187
216
}
188
217
default :
0 commit comments