Skip to content

[Flight] Track Debug Info from Synchronously Unwrapped Promises #33485

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jun 11, 2025

Conversation

sebmarkbage
Copy link
Collaborator

Stacked on #33482.

There's a flaw with getting information from the execution context of the ping. For the soft-deprecated "throw a promise" technique, this is a bit unreliable because you could in theory throw the same one multiple times. Similarly, a more fundamental flaw with that API is that it doesn't allow for tracking the information of Promises that are already synchronously able to resolve.

This stops tracking the async debug info in the case of throwing a Promise and only when you render a Promise. That means some loss of data but we should just warn for throwing a Promise anyway.

Instead, this also adds support for tracking use()d thenables and forwarding _debugInfo from then. This is done by extracting the info from the Promise after the fact instead of in the resolve so that it only happens once at the end after the pings are done.

This also supports passing the same Promise in multiple places and tracking the debug info at each location, even if it was already instrumented with a synchronous value by the time of the second use.

@github-actions github-actions bot added the React Core Team Opened by a member of the React Core Team label Jun 9, 2025
@react-sizebot
Copy link

react-sizebot commented Jun 9, 2025

Comparing: 56408a5...0943f8b

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.js = 6.68 kB 6.68 kB = 1.83 kB 1.83 kB
oss-stable/react-dom/cjs/react-dom-client.production.js = 530.58 kB 530.58 kB = 93.66 kB 93.66 kB
oss-experimental/react-dom/cjs/react-dom.production.js = 6.69 kB 6.69 kB = 1.83 kB 1.83 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js = 651.67 kB 651.67 kB = 114.77 kB 114.77 kB
facebook-www/ReactDOM-prod.classic.js = 676.61 kB 676.61 kB = 119.05 kB 119.05 kB
facebook-www/ReactDOM-prod.modern.js = 666.89 kB 666.89 kB = 117.44 kB 117.44 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-server.node.development.js +1.34% 170.28 kB 172.56 kB +1.05% 31.67 kB 32.01 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.unbundled.development.js +1.31% 181.68 kB 184.06 kB +1.07% 33.19 kB 33.55 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-server.node.development.js +1.30% 174.94 kB 177.22 kB +1.06% 32.13 kB 32.48 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.development.js +1.30% 182.83 kB 185.21 kB +1.06% 33.49 kB 33.84 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js +1.30% 182.89 kB 185.26 kB +1.05% 33.50 kB 33.85 kB
oss-experimental/react-server/cjs/react-server-flight.development.js +0.97% 116.87 kB 118.00 kB +0.71% 21.60 kB 21.76 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.development.js +0.78% 163.51 kB 164.79 kB +0.52% 30.20 kB 30.36 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.development.js +0.78% 164.05 kB 165.33 kB +0.52% 30.32 kB 30.48 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.development.js +0.77% 167.44 kB 168.72 kB +0.56% 30.75 kB 30.93 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.development.js +0.77% 167.46 kB 168.75 kB +0.57% 30.75 kB 30.92 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-server.browser.development.js +0.76% 155.77 kB 156.95 kB +0.54% 28.84 kB 29.00 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-server.edge.development.js +0.74% 159.69 kB 160.87 kB +0.52% 29.37 kB 29.52 kB
oss-stable-semver/react-server/cjs/react-server-flight.development.js +0.60% 109.46 kB 110.12 kB +0.39% 20.27 kB 20.34 kB
oss-stable/react-server/cjs/react-server-flight.development.js +0.60% 109.46 kB 110.12 kB +0.39% 20.27 kB 20.34 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.development.js +0.52% 156.08 kB 156.89 kB +0.35% 28.87 kB 28.97 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.development.js +0.52% 156.08 kB 156.89 kB +0.35% 28.87 kB 28.97 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.development.js +0.52% 156.63 kB 157.44 kB +0.32% 29.00 kB 29.09 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.development.js +0.52% 156.63 kB 157.44 kB +0.32% 29.00 kB 29.09 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.development.js +0.51% 159.66 kB 160.47 kB +0.31% 29.37 kB 29.46 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.development.js +0.51% 159.66 kB 160.47 kB +0.31% 29.37 kB 29.46 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.development.js +0.51% 159.69 kB 160.50 kB +0.31% 29.37 kB 29.46 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.development.js +0.51% 159.69 kB 160.50 kB +0.31% 29.37 kB 29.46 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.unbundled.development.js +0.49% 164.12 kB 164.93 kB +0.33% 29.89 kB 29.99 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.unbundled.development.js +0.49% 164.12 kB 164.93 kB +0.33% 29.89 kB 29.99 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.development.js +0.49% 165.27 kB 166.08 kB +0.34% 30.16 kB 30.26 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.development.js +0.49% 165.27 kB 166.08 kB +0.34% 30.16 kB 30.26 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js +0.49% 165.32 kB 166.13 kB +0.32% 30.18 kB 30.28 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js +0.49% 165.32 kB 166.13 kB +0.32% 30.18 kB 30.28 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-server.browser.development.js +0.48% 148.34 kB 149.06 kB +0.29% 27.51 kB 27.59 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-server.browser.development.js +0.48% 148.34 kB 149.06 kB +0.29% 27.51 kB 27.59 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-server.edge.development.js +0.47% 151.91 kB 152.63 kB +0.26% 28.02 kB 28.09 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-server.edge.development.js +0.47% 151.91 kB 152.63 kB +0.26% 28.02 kB 28.09 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-server.node.development.js +0.47% 152.71 kB 153.42 kB +0.38% 28.36 kB 28.46 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-server.node.development.js +0.47% 152.71 kB 153.42 kB +0.38% 28.36 kB 28.46 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-server.node.development.js +0.45% 157.38 kB 158.09 kB +0.34% 28.82 kB 28.92 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-server.node.development.js +0.45% 157.38 kB 158.09 kB +0.34% 28.82 kB 28.92 kB

Generated by 🚫 dangerJS against 0943f8b

// This is a temporary fork of the `use` implementation until we accept
// promises everywhere.
const thenable: Thenable<mixed> = (wakeable: any);
switch (thenable.status) {
case 'fulfilled':
case 'fulfilled': {
forwardDebugInfoFromThenable(request, task, thenable);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only applies to the internal temporary wrapper of Flight.

With this approach the Promise inside React.lazy is never tracked since it disappears and it behaves more like the throwing-a-promise approach. The idea is to convert these to just return Promises eventually though.

You can still assign manual _debugInfo to a lazy though.

However, we also only track Lazy in the child position. Lazy element.type (the only documented approach) are not tracked. So if you have some I/O to load some module with a Component in it, that wouldn't be tracked atm.

forwardDebugInfo(request, newTask, debugInfo);
}
}
forwardDebugInfoFromCurrentContext(request, newTask, thenable);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should just call forwardDebugInfoFromPromise here. The difference is that if this is not actually a native Promise but is resolved by a thenable which is in turn resolved by I/O (like the test in AsyncDebugInfo-test) or a native Promise then we can pick it up from the context it is resolved in.

The problem is just that now it's inconsistent because such as Thenable will only forward the information if it wasn't already resolved. If it was sync, the information would get lost.

So this is a bit inconsistent about when we expect Thenables to be manually instrumented with debugInfo like the ones coming out of Flight and when we can pick it up automatically.

The ones from FlightClient are also inconsistent in that they may pick up the I/O of the stream that loaded them when they weren't sync but if they were sync then only the forwarded debugInfo from the other server is included.

@sebmarkbage sebmarkbage force-pushed the iosync branch 4 times, most recently from 06d85b7 to ad831d1 Compare June 10, 2025 04:23
Unfortunately there's no public API to get to the internal symbols but
AsyncResource is conceptually the same class so we can repurpose its
method that does the same thing.
So might look for it on thenables when passed to use().
Before we wrap it in a Lazy since it won't be picked up by the lazy.
If we're forwarding debugInfo and also visiting the underlying
native Promise, make sure we're not emitting the same debugInfo twice.
This will be used to look at the diff to the next commit
The owner/stack disappears in this case because the use() is not a real await.
Then pass this as the default if there is no deeper await.

This means that use() behaves similar to await.
@sebmarkbage sebmarkbage merged commit ff93c44 into facebook:main Jun 11, 2025
241 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed React Core Team Opened by a member of the React Core Team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants