-
Notifications
You must be signed in to change notification settings - Fork 48.8k
[Flight] Forward debugInfo from awaited instrumented Promises #33415
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
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ca2f907
to
6e4b5b8
Compare
sebmarkbage
commented
Jun 3, 2025
} | ||
|
||
async function Component() { | ||
const data = await fetchThirdParty(ThirdPartyComponent); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the essence of this feature.
eps1lon
approved these changes
Jun 3, 2025
This turns into a cycle but it's fine as long as we just ignore it since it doesn't depend on I/O.
One thing that's a bit confusing is that an UnresolvedAwaitNode can resolve after its children since it's resolved after the then() function is invoked.
… it resolves Since this expando can't be assigned before it has been constructed, we can't do it in init. Unfortunately, we don't get passed the instance in promiseResolve() so we need to retain the instance. We must use a WeakRef since otherwise an infinite Promise that never resolves can't be GC:ed. If it does resolve, then it must be retained by the runtime until it finishes resolving at least. After that it might be too late so we have to then extract the information. A possible alternative strategy would be to install a setter on the Promise.
We do this whether this was blocked by I/O or not. This lets you create virtual nodes that behave like I/O.
That ensures that work spawned from the resulting render isn't included in the sequence. This helps isolate one render from another. TODO: We should probably mark startFlowing and abort too.
0e8ae99
to
3e749b9
Compare
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Stacked on #33403.
When a Promise is coming from React such as when it's passed from another environment, we should forward the debug information from that environment. We already do that when rendered as a child.
This makes it possible to also
await promise
and have the information from that instrumented promise carry through to the next render.This is a bit tricky because the current protocol is that we have to read it from the Promise after it resolves so it has time to be assigned to the promise.
async_hooks
doesn't pass us the instance (even though it has it) when it gets resolved so we need to keep it around. However, we have to be very careful because if we get this wrong it'll cause a memory leak since we retain things byasyncId
and then manually listen fordestroy()
which can only be called once a Promise is GC:ed, which it can't be if we retain it. We have to therefore use aWeakRef
in case it never resolves, and then read the_debugInfo
when it resolves. We could maybe install a setter or something instead but that's also heavy.The other issues is that we don't use native Promises in ReactFlightClient so our instrumented promises aren't picked up by the
async_hooks
implementation and so we never get a handle to our thenable instance. To solve this we can create a native wrapper only in DEV.