Deterministic updates#10715
Merged
Merged
Conversation
2d46bad to
20eb374
Compare
e2ab6b9 to
d47de1e
Compare
|
Deploy preview ready! Built with commit e2ab6b9 |
|
Deploy preview ready! Built with commit d0d798e |
9a31c91 to
d0d798e
Compare
d70d52e to
e3debd7
Compare
sebmarkbage
reviewed
Oct 10, 2017
| props: mixed, | ||
| renderExpirationTime: ExpirationTime, | ||
| ): State { | ||
| ): State | null { |
Contributor
There was a problem hiding this comment.
Why can this return null now?
sebmarkbage
reviewed
Oct 10, 2017
| ) { | ||
| const coalescedTime = insertAfter.expirationTime; | ||
| queue.expirationTime = update.expirationTime; | ||
| } |
Contributor
There was a problem hiding this comment.
If you expand the if (queue.last === null) { block to include the else condition instead of doing an early return, we can reuse this logic for both branches instead of duplicating.
e3debd7 to
1990983
Compare
This was referenced Nov 8, 2024
mrizwanashiq
pushed a commit
to mrizwanashiq/react
that referenced
this pull request
Jun 25, 2026
* Deterministic updates High priority updates typically require less work to render than low priority ones. It's beneficial to flush those first, in their own batch, before working on more expensive low priority ones. We do this even if a high priority is scheduled after a low priority one. However, we don't want this reordering of updates to affect the terminal state. State should be deterministic: once all work has been flushed, the final state should be the same regardless of how they were scheduled. To get both properties, we store updates on the queue in insertion order instead of priority order (always append). Then, when processing the queue, we skip over updates with insufficient priority. Instead of removing updates from the queue right after processing them, we only remove them if there are no unprocessed updates before it in the list. This means that updates may be processed more than once. As a bonus, the new implementation is simpler and requires less code. * Fix ceiling function Mixed up the operators. * Remove addUpdate, addReplaceState, et al These functions don't really do anything. Simpler to use a single insertUpdateIntoFiber function. Also splits scheduleUpdate into two functions: - scheduleWork traverses a fiber's ancestor path and updates their expiration times. - scheduleUpdate inserts an update into a fiber's update queue, then calls scheduleWork. * Remove getExpirationTime The last remaining use for getExpirationTime was for top-level async updates. I moved that check to scheduleUpdate instead. * Move UpdateQueue insertions back to class module Moves UpdateQueue related functions out of the scheduler and back into the class component module. It's a bit awkward that now we need to pass around createUpdateExpirationForFiber, too. But we can still do without addUpdate, replaceUpdate, et al. * Store callbacks as an array of Updates Simpler this way. Also moves commitCallbacks back to UpdateQueue module. * beginUpdateQueue -> processUpdateQueue * Updates should never have an expiration of NoWork * Rename expiration related functions * Fix update queue Flow types Gets rid of an unneccessary null check
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
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.
High priority updates typically require less work to render than low priority ones. It's beneficial to flush those first, in their own batch, before working on more expensive low priority ones. We do this even if a high priority is scheduled after a low priority one.
However, we don't want this reordering of updates to affect the terminal state. State should be deterministic: once all work has been flushed, the final state should be the same regardless of how they were scheduled.
To get both properties, we store updates on the queue in insertion order instead of priority order (always append). Then, when processing the queue, we skip over updates with insufficient priority. Instead of removing updates from the queue right after processing them, we only remove them if there are no unprocessed updates before it in the list.
This means that updates may be processed more than once.
As a bonus, the new implementation is simpler and requires less code.
To avoid conflicts, this works off my expiration times (#10426) and prerendering (#10624) PRs.