Skip to content

Commit fae80da

Browse files
committed
Assign implicit debug info to used thenables
This lets us pick up the debug info from a raw Promise as a child which is not covered by _debugThenables. It also lets us track some approximate start/end time of use():ed promises.
1 parent 3958d5d commit fae80da

File tree

2 files changed

+31
-0
lines changed

2 files changed

+31
-0
lines changed

packages/react-reconciler/src/ReactFiberThenable.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type {
1212
PendingThenable,
1313
FulfilledThenable,
1414
RejectedThenable,
15+
ReactIOInfo,
1516
} from 'shared/ReactTypes';
1617

1718
import type {LazyComponent as LazyComponentType} from 'react/src/ReactLazy';
@@ -22,6 +23,8 @@ import {getWorkInProgressRoot} from './ReactFiberWorkLoop';
2223

2324
import ReactSharedInternals from 'shared/ReactSharedInternals';
2425

26+
import {enableAsyncDebugInfo} from 'shared/ReactFeatureFlags';
27+
2528
import noop from 'shared/noop';
2629

2730
opaque type ThenableStateDev = {
@@ -154,6 +157,33 @@ export function trackUsedThenable<T>(
154157
}
155158
}
156159

160+
if (__DEV__ && enableAsyncDebugInfo && thenable._debugInfo === undefined) {
161+
// In DEV mode if the thenable that we observed had no debug info, then we add
162+
// an inferred debug info so that we're able to track its potential I/O uniquely.
163+
// We don't know the real start time since the I/O could have started much
164+
// earlier and this could even be a cached Promise. Could be misleading.
165+
const startTime = performance.now();
166+
const displayName = thenable.displayName;
167+
const ioInfo: ReactIOInfo = {
168+
name: typeof displayName === 'string' ? displayName : 'Promise',
169+
start: startTime,
170+
end: startTime,
171+
value: (thenable: any),
172+
// We don't know the requesting owner nor stack.
173+
};
174+
// We can infer the await owner/stack lazily from where this promise ends up
175+
// used. It can be used in more than one place so we can't assign it here.
176+
thenable._debugInfo = [{awaited: ioInfo}];
177+
// Track when we resolved the Promise as the approximate end time.
178+
if (thenable.status !== 'fulfilled' && thenable.status !== 'rejected') {
179+
const trackEndTime = () => {
180+
// $FlowFixMe[cannot-write]
181+
ioInfo.end = performance.now();
182+
};
183+
thenable.then(trackEndTime, trackEndTime);
184+
}
185+
}
186+
157187
// We use an expando to track the status and result of a thenable so that we
158188
// can synchronously unwrap the value. Think of this as an extension of the
159189
// Promise API, or a custom interface that is a superset of Thenable.

packages/shared/ReactTypes.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ interface ThenableImpl<T> {
108108
onFulfill: (value: T) => mixed,
109109
onReject: (error: mixed) => mixed,
110110
): void | Wakeable;
111+
displayName?: string;
111112
}
112113
interface UntrackedThenable<T> extends ThenableImpl<T> {
113114
status?: void;

0 commit comments

Comments
 (0)