You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Resuming is the ability to re-use fibers after they are interrupted by a higher-priority update. Take the following scenario: A component is updated at a normal, async priority. Before the update is finished processing, a higher-priority update is scheduled (let's say it's synchronous, though it could also be a higher-priority async update). The sync update interrupts the async update, leaving it unfinished. After the sync update finishes, we go back to processing the interrupted, async update. It's possible, and even likely, that the interrupted work wasn't touched by the sync work and can be resumed without starting over completely.
This is an important optimization for several async features we have in mind, including error handling, blockers, pre-rendering, and hidden priority.
We used to have an implementation of resuming that mostly worked but had some bugs. A few months ago, I spent some time identifying the bugs using fuzz testing and fixing them by iterating on the existing algorithm. I eventually got a version working that passed all the tests. But even this version didn't have all of the features we wanted, and the algorithm seemed inherently flawed. So we decided it would be best to scrap the existing algorithm and revisit resuming in the future.
We now believe we have a better idea of how resuming should work. I'm going to split the work into multiple PRs, and use this issue to keep track of our progress.
My apologies if some of my descriptions are hard to follow. It can be difficult to describe without resorting to jargon. I'll iterate on this issue as I work.
Always reconcile against current child set (#11564)
This is a small refactor that reflects what we already do without resuming: the set we reconcile against is always the current set. In the reverted resuming algorithm, the set we reconcile against was sometimes a work-in-progress set, and there are a few code paths that are left over from that implementation.
Stash interrupted children
When cloning a work-in-progress fiber from current, and there is already an existing work-in-progress that was interrupted, stash the interrupted work-in-progress children (and corresponding fields) in case we can reuse them later. In begin phase, add an additional check to see if incoming props/state match the interrupted props/state. If so, bail out and re-use the interrupted children. If not, the interrupted children are no longer useful, because we're about to re-render the parent and overwrite them. (Unmounted fibers actually can be re-used even if we re-render the parent; see next step.)
This gets us back to the same functionality we had in the old resuming algorithm. We can now resume interrupted children if we come back to it at the same priority at which it was originally rendered. The main limitation is that the work is lost if the parent is re-rendered at a higher priority.
*Need a way to distinguish between a work-in-progress fiber and the "previous current" fiber
Pool unmounted, interrupted children so they can resume even if parent re-renders at higher priority
When a fiber is about to be re-rendered, and there are interrupted children that could not be reused, search through the interrupted children and find the ones that are unmounted (don't have an alternate). Stash the unmounted children in a separate set; they can be kept around indefinitely without being overwritten. This set acts like a pool of children. The next time the parent is re-rendered at the priority of the interrupted children, check the pool for matches before creating new fibers.
The text was updated successfully, but these errors were encountered:
im digging through opened umbrella issues and this one is seems a little bit outdated. perhaps it should be merged into some other more relevant issue?
Resuming is the ability to re-use fibers after they are interrupted by a higher-priority update. Take the following scenario: A component is updated at a normal, async priority. Before the update is finished processing, a higher-priority update is scheduled (let's say it's synchronous, though it could also be a higher-priority async update). The sync update interrupts the async update, leaving it unfinished. After the sync update finishes, we go back to processing the interrupted, async update. It's possible, and even likely, that the interrupted work wasn't touched by the sync work and can be resumed without starting over completely.
This is an important optimization for several async features we have in mind, including error handling, blockers, pre-rendering, and hidden priority.
We used to have an implementation of resuming that mostly worked but had some bugs. A few months ago, I spent some time identifying the bugs using fuzz testing and fixing them by iterating on the existing algorithm. I eventually got a version working that passed all the tests. But even this version didn't have all of the features we wanted, and the algorithm seemed inherently flawed. So we decided it would be best to scrap the existing algorithm and revisit resuming in the future.
We now believe we have a better idea of how resuming should work. I'm going to split the work into multiple PRs, and use this issue to keep track of our progress.
My apologies if some of my descriptions are hard to follow. It can be difficult to describe without resorting to jargon. I'll iterate on this issue as I work.
Always reconcile against current child set (#11564)
This is a small refactor that reflects what we already do without resuming: the set we reconcile against is always the current set. In the reverted resuming algorithm, the set we reconcile against was sometimes a work-in-progress set, and there are a few code paths that are left over from that implementation.
Stash interrupted children
When cloning a work-in-progress fiber from current, and there is already an existing work-in-progress that was interrupted, stash the interrupted work-in-progress children (and corresponding fields) in case we can reuse them later. In begin phase, add an additional check to see if incoming props/state match the interrupted props/state. If so, bail out and re-use the interrupted children. If not, the interrupted children are no longer useful, because we're about to re-render the parent and overwrite them. (Unmounted fibers actually can be re-used even if we re-render the parent; see next step.)
This gets us back to the same functionality we had in the old resuming algorithm. We can now resume interrupted children if we come back to it at the same priority at which it was originally rendered. The main limitation is that the work is lost if the parent is re-rendered at a higher priority.
*Need a way to distinguish between a work-in-progress fiber and the "previous current" fiber
Pool unmounted, interrupted children so they can resume even if parent re-renders at higher priority
When a fiber is about to be re-rendered, and there are interrupted children that could not be reused, search through the interrupted children and find the ones that are unmounted (don't have an alternate). Stash the unmounted children in a separate set; they can be kept around indefinitely without being overwritten. This set acts like a pool of children. The next time the parent is re-rendered at the priority of the interrupted children, check the pool for matches before creating new fibers.
The text was updated successfully, but these errors were encountered: