Skip to content
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

Move renderDidSuspend logic to throwException #25852

Closed

Conversation

acdlite
Copy link
Collaborator

@acdlite acdlite commented Dec 8, 2022

Depends on bugfix in #25851

This is part of a larger refactor I'm doing to simplify how this logic works, since it's currently spread out and duplicated across several different parts of the work loop.

When a Suspense boundary shows a fallback, and it wasn't already showing a fallback, we mark the render as suspended. Knowing whether the in-progress tree includes a new fallback is useful for several reasons: we can choose to interrupt the current render and switch to a different task, we can suspend the work loop until more data becomes available, we can throttle the appearance of multiple fallback states, and so on.

Currently this logic happens in the complete phase, but because we use renderDidSuspendWithDelay as a signal to interrupt the work loop, it's best to do it early as we possibly can.

A subsequent step will move the logic even earlier, as soon as we attempt to unwrap an unresolved promise, so that use can determine whether it's OK to pause the entire work loop and wait for the promise. There's some existing code that attempts to do this but it doesn't have parity with how renderDidSuspend works, which is the main motivation for this series of refactors.

I found this bug when working on a different task.

`pingSuspendedRoot` sometimes calls `prepareFreshStack` to interupt the
work-in-progress tree and force a restart from the root. The idea is 
that if the current render is already in a state where it be blocked
from committing, and there's new data that could unblock it, we might
as well restart from the beginning.

The problem is that this is only safe to do if `pingSuspendedRoot` is
called from a non-React task, like an event handler or a microtask.
While this is usually the case, it's entirely possible for a thenable
to resolve (i.e. to call `pingSuspendedRoot`) synchronously while the
render phase is already executing. If that happens, and work loop
attempts to unwind the stack, it causes the render phase to crash.

This commit adds a regression test that reproduces one of
these scenarios.
This is part of a larger refactor I'm doing to simplify how this logic
works, since it's currently spread out and duplicated across several
different parts of the work loop.

When a Suspense boundary shows a fallback, and it wasn't already showing
a fallback, we mark the render as suspended. Knowing whether the
in-progress tree includes a new fallback is useful for several reasons:
we can choose to interrupt the current render and switch to a different
task, we can suspend the work loop until more data becomes available, we
can throttle the appearance of multiple fallback states, and so on.

Currently this logic happens in the complete phase, but because we use
renderDidSuspendWithDelay as a signal to interrupt the work loop, it's
best to do it early as we possibly can.

A subsequent step will move the logic even earlier, as soon as we
attempt to unwrap an unresolved promise, so that `use` can determine
whether it's OK to pause the entire work loop and wait for the promise.
There's some existing code that attempts to do this but it doesn't have
parity with how `renderDidSuspend` works, which is the main motivation
for this series of refactors.
@facebook-github-bot facebook-github-bot added CLA Signed React Core Team Opened by a member of the React Core Team labels Dec 8, 2022
@sizebot
Copy link

sizebot commented Dec 8, 2022

Comparing: 7c39922...ca09d0c

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.min.js +0.02% 154.26 kB 154.29 kB +0.14% 48.94 kB 49.01 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js +0.02% 156.18 kB 156.22 kB +0.13% 49.60 kB 49.67 kB
facebook-www/ReactDOM-prod.classic.js = 532.79 kB 532.53 kB = 94.91 kB 94.90 kB
facebook-www/ReactDOM-prod.modern.js = 517.88 kB 517.63 kB = 92.70 kB 92.68 kB
facebook-www/ReactDOMForked-prod.classic.js = 532.79 kB 532.53 kB = 94.91 kB 94.90 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
facebook-www/ReactTestRenderer-dev.modern.js +0.24% 780.38 kB 782.29 kB +0.33% 167.60 kB 168.16 kB
facebook-www/ReactTestRenderer-dev.classic.js +0.24% 780.38 kB 782.29 kB +0.33% 167.60 kB 168.16 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.development.js +0.23% 749.65 kB 751.38 kB +0.30% 162.59 kB 163.08 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.development.js +0.23% 749.67 kB 751.41 kB +0.30% 162.62 kB 163.10 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.development.js +0.23% 749.77 kB 751.51 kB +0.30% 162.65 kB 163.13 kB
oss-stable-semver/react-test-renderer/umd/react-test-renderer.development.js +0.23% 785.32 kB 787.12 kB +0.32% 164.31 kB 164.84 kB
oss-stable/react-test-renderer/umd/react-test-renderer.development.js +0.23% 785.34 kB 787.14 kB +0.32% 164.34 kB 164.87 kB
oss-experimental/react-test-renderer/umd/react-test-renderer.development.js +0.23% 785.45 kB 787.24 kB +0.32% 164.37 kB 164.90 kB
oss-stable-semver/react-art/cjs/react-art.development.js +0.23% 770.04 kB 771.78 kB +0.30% 166.60 kB 167.10 kB
oss-stable/react-art/cjs/react-art.development.js +0.23% 770.07 kB 771.81 kB +0.30% 166.62 kB 167.12 kB
facebook-react-native/react-test-renderer/cjs/ReactTestRenderer-dev.js +0.22% 763.99 kB 765.70 kB +0.32% 164.26 kB 164.79 kB
oss-experimental/react-art/cjs/react-art.development.js +0.22% 777.86 kB 779.60 kB +0.30% 168.02 kB 168.52 kB
facebook-www/ReactART-dev.modern.js +0.22% 870.84 kB 872.75 kB +0.30% 184.31 kB 184.85 kB
facebook-www/ReactART-dev.classic.js +0.22% 881.30 kB 883.22 kB +0.29% 186.42 kB 186.96 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler.development.js +0.21% 849.83 kB 851.60 kB +0.28% 180.82 kB 181.32 kB
oss-stable/react-reconciler/cjs/react-reconciler.development.js +0.21% 849.86 kB 851.63 kB +0.27% 180.84 kB 181.34 kB
oss-experimental/react-reconciler/cjs/react-reconciler.development.js +0.21% 857.65 kB 859.42 kB +0.27% 182.28 kB 182.78 kB
oss-stable-semver/react-art/umd/react-art.development.js +0.20% 877.48 kB 879.28 kB +0.29% 184.80 kB 185.32 kB
oss-stable/react-art/umd/react-art.development.js +0.20% 877.51 kB 879.30 kB +0.29% 184.82 kB 185.35 kB
react-native/implementations/ReactFabric-dev.js +0.20% 842.90 kB 844.61 kB +0.28% 183.29 kB 183.81 kB
oss-experimental/react-art/umd/react-art.development.js +0.20% 885.74 kB 887.54 kB +0.29% 186.25 kB 186.78 kB
react-native/implementations/ReactNativeRenderer-dev.js +0.20% 852.33 kB 854.04 kB +0.28% 185.65 kB 186.17 kB

Generated by 🚫 dangerJS against ca09d0c

@acdlite
Copy link
Collaborator Author

acdlite commented Feb 9, 2023

Landed as part of #25922

@acdlite acdlite closed this Feb 9, 2023
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.

3 participants