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

Synchronously restart when an error is thrown during async rendering #13041

Merged
merged 1 commit into from
Jun 14, 2018

Conversation

acdlite
Copy link
Collaborator

@acdlite acdlite commented Jun 13, 2018

In async mode, events are interleaved with rendering. If one of those events mutates state that is later accessed during render, it can lead to inconsistencies/tearing.

Restarting the render from the root is often sufficient to fix the inconsistency. We'll flush the restart synchronously to prevent yet another mutation from happening during an interleaved event.

We'll only restart during an async render. Sync renders are already sync, so there's no benefit in restarting. (Unless a mutation happens during the render phase, but we don't support that.)

@pull-bot
Copy link

pull-bot commented Jun 13, 2018

ReactDOM: size: 🔺+0.2%, gzip: 🔺+0.2%

Details of bundled changes.

Comparing: 9bda7b2...7cffa51

react-dom

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-dom.development.js +0.2% +0.2% 627.24 KB 628.31 KB 146.09 KB 146.38 KB UMD_DEV
react-dom.production.min.js 🔺+0.2% 🔺+0.2% 95.36 KB 95.51 KB 30.84 KB 30.89 KB UMD_PROD
react-dom.development.js +0.2% +0.2% 611.61 KB 612.68 KB 142.08 KB 142.37 KB NODE_DEV
react-dom.production.min.js 🔺+0.2% 🔺+0.1% 93.85 KB 94 KB 29.78 KB 29.82 KB NODE_PROD
ReactDOM-dev.js +0.2% +0.2% 621.17 KB 622.26 KB 141.48 KB 141.79 KB FB_WWW_DEV
ReactDOM-prod.js -0.0% 🔺+0.2% 269.63 KB 269.57 KB 50.54 KB 50.61 KB FB_WWW_PROD
react-dom.profiling.min.js +0.2% +0.1% 94.76 KB 94.91 KB 30.08 KB 30.12 KB NODE_PROFILING

react-art

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-art.development.js +0.3% +0.3% 408.33 KB 409.4 KB 91.22 KB 91.51 KB UMD_DEV
react-art.production.min.js 🔺+0.2% 🔺+0.2% 82.2 KB 82.35 KB 25.29 KB 25.35 KB UMD_PROD
react-art.development.js +0.3% +0.4% 334.18 KB 335.25 KB 72.29 KB 72.61 KB NODE_DEV
react-art.production.min.js 🔺+0.3% 🔺+0.2% 46.56 KB 46.71 KB 14.48 KB 14.5 KB NODE_PROD
ReactART-dev.js +0.3% +0.5% 326.84 KB 327.93 KB 68.24 KB 68.56 KB FB_WWW_DEV
ReactART-prod.js -0.0% 🔺+0.4% 142.13 KB 142.07 KB 24.24 KB 24.33 KB FB_WWW_PROD

react-test-renderer

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-test-renderer.development.js +0.3% +0.4% 341.74 KB 342.81 KB 73.99 KB 74.31 KB UMD_DEV
react-test-renderer.production.min.js 🔺+0.3% 🔺+0.5% 47.1 KB 47.25 KB 14.47 KB 14.54 KB UMD_PROD
react-test-renderer.development.js +0.3% +0.4% 332.58 KB 333.65 KB 71.26 KB 71.58 KB NODE_DEV
react-test-renderer.production.min.js 🔺+0.3% 🔺+0.4% 46.21 KB 46.36 KB 14.03 KB 14.09 KB NODE_PROD
ReactTestRenderer-dev.js +0.3% +0.4% 338.37 KB 339.46 KB 70.88 KB 71.2 KB FB_WWW_DEV

react-reconciler

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-reconciler.development.js +0.3% +0.4% 324.79 KB 325.86 KB 68.84 KB 69.13 KB NODE_DEV
react-reconciler.production.min.js 🔺+0.3% 🔺+0.4% 46.32 KB 46.47 KB 13.8 KB 13.86 KB NODE_PROD
react-reconciler-persistent.development.js +0.3% +0.4% 323.41 KB 324.48 KB 68.25 KB 68.54 KB NODE_DEV
react-reconciler-persistent.production.min.js 🔺+0.3% 🔺+0.4% 46.33 KB 46.48 KB 13.81 KB 13.86 KB NODE_PROD

react-native-renderer

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
ReactNativeRenderer-dev.js +0.2% +0.3% 458.13 KB 459.22 KB 100.16 KB 100.48 KB RN_FB_DEV
ReactNativeRenderer-prod.js -0.0% 🔺+0.2% 205.67 KB 205.61 KB 35.9 KB 35.99 KB RN_FB_PROD
ReactNativeRenderer-dev.js +0.2% +0.3% 457.84 KB 458.93 KB 100.1 KB 100.41 KB RN_OSS_DEV
ReactNativeRenderer-prod.js 🔺+0.2% 🔺+0.2% 197.68 KB 198.17 KB 34.46 KB 34.55 KB RN_OSS_PROD
ReactFabric-dev.js +0.2% +0.3% 448.45 KB 449.54 KB 97.78 KB 98.09 KB RN_FB_DEV
ReactFabric-prod.js 🔺+0.3% 🔺+0.3% 189.96 KB 190.45 KB 33.08 KB 33.17 KB RN_FB_PROD
ReactFabric-dev.js +0.2% +0.3% 448.48 KB 449.57 KB 97.79 KB 98.11 KB RN_OSS_DEV
ReactFabric-prod.js 🔺+0.3% 🔺+0.3% 190 KB 190.48 KB 33.1 KB 33.18 KB RN_OSS_PROD
ReactNativeRenderer-profiling.js +0.2% +0.2% 200.22 KB 200.71 KB 35.01 KB 35.09 KB RN_OSS_PROFILING
ReactFabric-profiling.js +0.3% +0.3% 192.22 KB 192.7 KB 33.57 KB 33.66 KB RN_OSS_PROFILING

Generated by 🚫 dangerJS

Copy link
Collaborator

@sebmarkbage sebmarkbage left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make sure it gets reset if an update gets scheduled before it's done.

@sebmarkbage
Copy link
Collaborator

In a follow up we should see if we can only do the error replaying (invoke guarded callback) by reusing these two phases. The async phase only uses real try/catch. If it is a Promise, it'll catch it. If it is an error, it will always retry anyway. So the second one can only use invoke guarded callback.

In async mode, events are interleaved with rendering. If one of those
events mutates state that is later accessed during render, it can lead
to inconsistencies/tearing.

Restarting the render from the root is often sufficient to fix the
inconsistency. We'll flush the restart synchronously to prevent yet
another mutation from happening during an interleaved event.

We'll only restart during an async render. Sync renders are already
sync, so there's no benefit in restarting. (Unless a mutation happens
during the render phase, but we don't support that.)
@acdlite acdlite merged commit 9bd4d1f into facebook:master Jun 14, 2018
acdlite added a commit to acdlite/react that referenced this pull request Jun 15, 2018
Since facebook#13041, we always replay the entire render phase before handling
an error. We can wrap this in invokeGuardedCallback, instead of
wrapping and replaying only the render phase of the failed fiber.

Now we don't have to stash the work-in-progress fiber fields, which was
quite fragile.
acdlite added a commit to acdlite/react that referenced this pull request Jun 15, 2018
Since facebook#13041, we always replay the entire render phase before handling
an error. We can wrap this in invokeGuardedCallback, instead of
wrapping and replaying only the render phase of the failed fiber.

Now we don't have to stash the work-in-progress fiber fields, which was
quite fragile.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants