Skip to content

Commit 033edca

Browse files
authored
[Flight] Yolo Retention of Promises (facebook#33737)
Follow up to facebook#33736. If we need to save on CPU/memory pressure, we can instead just pray and hope that a Promise doesn't get garbage collected before we need to read it. This can cause fragile access to the Promise value in devtools especially if it's a slow and pressured render. Basically, you'd have to hope that GC doesn't run after the inner await finishes its microtask callback and before the resolution of the component being rendered is invoked.
1 parent e6dc25d commit 033edca

File tree

1 file changed

+1
-45
lines changed

1 file changed

+1
-45
lines changed

packages/react-server/src/ReactFlightServerConfigDebugNode.js

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,6 @@ const getAsyncId = AsyncResource.prototype.asyncId;
3737
const pendingOperations: Map<number, AsyncSequence> =
3838
__DEV__ && enableAsyncDebugInfo ? new Map() : (null: any);
3939

40-
// This is a weird one. This map, keeps a dependent Promise alive if the child Promise is still alive.
41-
// A PromiseNode/AwaitNode cannot hold a strong reference to its own Promise because then it'll never get
42-
// GC:ed. We only need it if a dependent AwaitNode points to it. We could put a reference in the Node
43-
// but that would require a GC pass between every Node that gets destroyed. I.e. the root gets destroy()
44-
// called on it and then that release it from the pendingOperations map which allows the next one to GC
45-
// and so on. By putting this relationship in a WeakMap this could be done as a single pass in the VM.
46-
// We don't actually ever have to read from this map since we have WeakRef reference to these Promises
47-
// if they're still alive. It's also optional information so we could just expose only if GC didn't run.
48-
const awaitedPromise: WeakMap<
49-
Promise<any>,
50-
Promise<any> | [Promise<any>, Promise<any>],
51-
> = __DEV__ && enableAsyncDebugInfo ? new WeakMap() : (null: any);
52-
5340
// Keep the last resolved await as a workaround for async functions missing data.
5441
let lastRanAwait: null | AwaitNode = null;
5542

@@ -92,38 +79,6 @@ export function initAsyncDebugInfo(): void {
9279
// We don't track awaits on things that started outside our tracked scope.
9380
return;
9481
}
95-
let retain: null | Promise<any> | [Promise<any>, Promise<any>] =
96-
null;
97-
const triggerPromiseRef = trigger.promise;
98-
if (triggerPromiseRef !== null) {
99-
const triggerPromise = triggerPromiseRef.deref();
100-
if (triggerPromise !== undefined) {
101-
// Keep the awaited Promise alive as long as the child is alive so we can
102-
// trace its value at the end.
103-
retain = triggerPromise;
104-
}
105-
}
106-
107-
const current = pendingOperations.get(currentAsyncId);
108-
if (current !== undefined) {
109-
const currentPromiseRef = current.promise;
110-
if (currentPromiseRef !== null) {
111-
const currentPromise = currentPromiseRef.deref();
112-
if (currentPromise !== undefined) {
113-
// Keep the previous Promise alive as long as the child is alive so we can
114-
// trace its value at the end.
115-
if (retain === null) {
116-
retain = currentPromise;
117-
} else {
118-
retain = [(retain: any), currentPromise];
119-
}
120-
}
121-
}
122-
}
123-
124-
if (retain !== null) {
125-
awaitedPromise.set(resource, retain);
126-
}
12782
// If the thing we're waiting on is another Await we still track that sequence
12883
// so that we can later pick the best stack trace in user space.
12984
let stack = null;
@@ -158,6 +113,7 @@ export function initAsyncDebugInfo(): void {
158113
}
159114
}
160115
}
116+
const current = pendingOperations.get(currentAsyncId);
161117
node = ({
162118
tag: UNRESOLVED_AWAIT_NODE,
163119
owner: resolveOwner(),

0 commit comments

Comments
 (0)