-
Notifications
You must be signed in to change notification settings - Fork 46.9k
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
Add scheduleMicrotaskCallback, use it to schedule flushSync for discrete update priority #20658
Conversation
Do you mind splitting the queueMicrotask stuff into its own PR? Then stack the rest on top |
I wasn't thinking that queueMicrotask would be integrated into the scheduler since it's not natively neither. They're separate things. Is there a reason that this isn't literally |
@@ -27,6 +27,7 @@ import { | |||
const { | |||
unstable_runWithPriority: Scheduler_runWithPriority, | |||
unstable_scheduleCallback: Scheduler_scheduleCallback, | |||
unstable_scheduleMicrotaskCallback: Scheduler_scheduleMicrotaskCallback, |
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.
I don't have a strong opinion on the API.
An alternative may be:
scheduleCallback(
MicrotaskPriority,
performSyncWorkOnRoot.bind(null, root),
);
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.
I think it should just be queueMicrotask. It's just a literal polyfill for that function which is already a standard. I'm a little doubtful that it'll be part of the Scheduler spec eventually since microtasks are layered differently spec wise, but even if it was that API has more overhead than a simple call into native.
@@ -232,7 +232,12 @@ describe('ReactIncrementalErrorHandling', () => { | |||
expect(ReactNoop.getChildren()).toEqual([span('Caught an error: oops!')]); | |||
}); | |||
|
|||
it("retries at a lower priority if there's additional pending work", () => { | |||
it("retries at a lower priority if there's additional pending work", async () => { | |||
if (gate(flags => flags.enableDiscreteEventMicroTasks)) { |
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.
@acdlite there are 3 tests disabled in this file, which I'm not sure how to fix without an microtask integration with toFlushAndYieldThrough
}); | ||
// Low pri | ||
root.render(<App text="A" shouldSuspend={true} />); | ||
if (gate(flags => flags.enableDiscreteEventMicroTasks)) { |
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.
These tests are not as good as before since we can't flush only the microtasks without all the other work.
Promise.resolve(null) | ||
.then(callback) | ||
.catch(e => { | ||
setTimeout(() => { |
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.
Important note: when we fallback to the promise implementation, we have to push errors into a new task or they'll be treated as unhandled promise rejections. I'm using the MDN polyfill version here, but we may want to schedule a MicrotaskPriority task instead, since these can be pushed behind other work.
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.
Not too concerned since it really only affects debugging and all modern browsers support queueMicrotask
. (God I hate this problem so much, though.)
@acdlite I can break this up, do you agree with @sebmarkbage that this shouldn't be in scheduler? If so, where should this go? |
I'm not sure where this should live but the part I don't get is why it's adding it to a scheduler queue? It seems like it should just be straight passthrough. It could be part of scheduler just to have one convenient way to get all these polyfills but now I'm skeptical it belongs there because it's tempting to integrate it unnecessarily and you have to always pull in the whole scheduler even if you depend on only this function. Nobody else is going to do that so there's no code sharing benefit. |
I would do it as an export of Scheduler for now. import queueMicrotask from 'scheduler/queueMicrotask'; |
Maybe it should just be a host config? |
Arguably all of the Scheduler methods should be host configs so we can possibly swap them out |
If we publish a timer polyfill we'll likely put that in the Scheduler package, too. We also may need to publish our own Promise polyfill for older browsers, so it integrates properly with our message event listener. I don't think we're going to solve all of that in this PR. |
I was thinking that for React Native it might be that discrete !== micro task. Since we can implement it differently there and some discrete might need to consider the thread they're on. Seems like Host Config might be the better injection point there but could be layered twice. Like the DOM host config defers to scheduler/queueMicrotask. Where as for scheduling work, it makes sense to have a shared one since it's public API and integrates with other scheduled things. |
We could call it like |
@sebmarkbage one key benefit of using the scheduler for this right now is that we call |
We can't cancel discrete work though. So that won't make sense anyway. |
@rickhanlonii The suggestion is to not cancel the microtask once it's scheduled 😬 |
We do need to cancel non-discrete tasks once the priority changes to discrete (like a child that uses startTransition and a parent that doesn't), so we'll need to only cancel the non-discrete tasks somehow, which seems non-trivial to unblock this. |
Maybe it is trivial, and we just don't store the microtask on the node. |
Let's continue in the separate PR? That way we can focus on that by itself |
41a02ed
to
41eeb82
Compare
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. Latest deployment of this branch, based on commit cf96b27:
|
@@ -369,19 +373,24 @@ describe('SimpleEventPlugin', function() { | |||
click(); | |||
|
|||
// Flush the remaining work | |||
Scheduler.unstable_flushAll(); | |||
await TestUtils.act(async () => { |
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.
act should wrap click
41eeb82
to
cf96b27
Compare
Thanks for the feedback. I went with the HostConfig strategy and created a new stack of PRs with the head here. |
Overview
We're researching a few updates to discrete events:
scheduleMicrotaskCallback
to the scheduler along with a basicqueueMicrotask
pollyfill.Note: there are a few tests that changes and others that are still disabled. Hope to work those use cases out in review.