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

Resume immediately pinged fiber without unwinding #25074

Merged
merged 4 commits into from
Aug 12, 2022

Conversation

acdlite
Copy link
Collaborator

@acdlite acdlite commented Aug 10, 2022

If a fiber suspends, and is pinged immediately in a microtask (or a regular task that fires before React resumes rendering), try rendering the same fiber again without unwinding the stack. This can be super helpful when working with promises and async-await, because even if the outermost promise hasn't been cached before, the underlying data may have been preloaded. In many cases, we can continue rendering immediately without having to show a fallback.

This optimization should work during any concurrent (time-sliced) render. It doesn't work during discrete updates because those are semantically required to finish synchronously — those get the current behavior.

Instead of using an imperative method `requestYield` to ask Scheduler to
yield to the main thread, we can assume that any time a Scheduler task
returns a continuation callback, it's because it wants to yield to the
main thread. We can assume the task already checked some condition that
caused it to return a continuation, so we don't need to do any
additional checks — we can immediately yield and schedule a new task
for the continuation.

The replaces the `requestYield` API that I added in ca990e9.
I need to be able to yield to the main thread in between when an error
is thrown and when the stack is unwound. (This is the motivation behind
the refactor, but it isn't implemented in this commit.) Currently the
unwind is inlined directly into `handleError`.

Instead, I've moved the unwind logic into the main work loop. At the
very beginning of the function, we check to see if the work-in-progress
is in a "suspended" state — that is, whether it needs to be unwound. If
it is, we will enter the unwind phase instead of the begin phase.

We only need to perform this check when we first enter the work loop:
at the beginning of a Scheduler chunk, or after something throws. We
don't need to perform it after every unit of work.
@facebook-github-bot facebook-github-bot added CLA Signed React Core Team Opened by a member of the React Core Team labels Aug 10, 2022
@@ -998,8 +1001,14 @@ describe('ReactSuspenseWithNoopRenderer', () => {
'Loading...',
// Once we've completed the boundary we restarted.
'Async',
'Sibling',
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

These changes are because this test happened to produce a scenario where a fiber is immediately pinged. It's not relevant to the test so I modified it slightly.

@sizebot
Copy link

sizebot commented Aug 10, 2022

Comparing: 17e2a15...eefdb73

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.22% 134.02 kB 134.32 kB = 42.92 kB 42.92 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js +0.21% 139.58 kB 139.88 kB +0.03% 44.54 kB 44.55 kB
facebook-www/ReactDOM-prod.classic.js +0.04% 474.91 kB 475.11 kB +0.02% 84.86 kB 84.87 kB
facebook-www/ReactDOM-prod.modern.js +0.04% 460.15 kB 460.35 kB +0.02% 82.62 kB 82.64 kB
facebook-www/ReactDOMForked-prod.classic.js +0.04% 474.91 kB 475.11 kB +0.02% 84.86 kB 84.87 kB
facebook-www/SchedulerPostTask-dev.classic.js = 7.17 kB 7.01 kB = 2.14 kB 2.11 kB
facebook-www/SchedulerPostTask-dev.modern.js = 7.17 kB 7.01 kB = 2.14 kB 2.11 kB
oss-experimental/scheduler/cjs/scheduler-unstable_post_task.development.js = 7.04 kB 6.88 kB = 2.12 kB 2.08 kB
oss-stable-semver/scheduler/cjs/scheduler-unstable_post_task.development.js = 7.04 kB 6.88 kB = 2.12 kB 2.08 kB
oss-stable/scheduler/cjs/scheduler-unstable_post_task.development.js = 7.04 kB 6.88 kB = 2.12 kB 2.08 kB
oss-experimental/scheduler/cjs/scheduler-unstable_post_task.production.min.js = 2.02 kB 1.97 kB = 0.86 kB 0.85 kB
oss-stable-semver/scheduler/cjs/scheduler-unstable_post_task.production.min.js = 2.02 kB 1.97 kB = 0.86 kB 0.85 kB
oss-stable/scheduler/cjs/scheduler-unstable_post_task.production.min.js = 2.02 kB 1.97 kB = 0.86 kB 0.85 kB
oss-experimental/scheduler/umd/scheduler.development.js = 5.14 kB 4.90 kB = 0.88 kB 0.87 kB
oss-stable-semver/scheduler/umd/scheduler.development.js = 5.14 kB 4.90 kB = 0.88 kB 0.87 kB
oss-stable/scheduler/umd/scheduler.development.js = 5.14 kB 4.90 kB = 0.88 kB 0.87 kB
oss-experimental/scheduler/umd/scheduler.production.min.js = 4.88 kB 4.63 kB = 0.88 kB 0.87 kB
oss-experimental/scheduler/umd/scheduler.profiling.min.js = 4.88 kB 4.63 kB = 0.88 kB 0.87 kB
oss-stable-semver/scheduler/umd/scheduler.production.min.js = 4.88 kB 4.63 kB = 0.88 kB 0.87 kB
oss-stable-semver/scheduler/umd/scheduler.profiling.min.js = 4.88 kB 4.63 kB = 0.88 kB 0.87 kB
oss-stable/scheduler/umd/scheduler.production.min.js = 4.88 kB 4.63 kB = 0.88 kB 0.87 kB
oss-stable/scheduler/umd/scheduler.profiling.min.js = 4.88 kB 4.63 kB = 0.88 kB 0.87 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
facebook-www/SchedulerMock-prod.classic.js +1.89% 12.23 kB 12.46 kB +1.83% 2.89 kB 2.95 kB
facebook-www/SchedulerMock-prod.modern.js +1.89% 12.23 kB 12.46 kB +1.83% 2.89 kB 2.95 kB
facebook-react-native/scheduler/cjs/SchedulerMock-prod.js +1.88% 11.96 kB 12.18 kB +1.85% 2.81 kB 2.86 kB
facebook-react-native/scheduler/cjs/Scheduler-prod.js +1.15% 10.34 kB 10.46 kB +0.88% 2.61 kB 2.63 kB
facebook-react-native/scheduler/cjs/Scheduler-profiling.js +1.09% 10.95 kB 11.06 kB +0.98% 2.75 kB 2.78 kB
facebook-www/Scheduler-prod.classic.js +1.07% 11.65 kB 11.77 kB +0.94% 2.89 kB 2.91 kB
facebook-www/Scheduler-prod.modern.js +1.07% 11.65 kB 11.77 kB +0.94% 2.89 kB 2.91 kB
oss-experimental/scheduler/cjs/scheduler-unstable_mock.development.js +0.73% 17.62 kB 17.75 kB +0.92% 4.23 kB 4.27 kB
oss-stable-semver/scheduler/cjs/scheduler-unstable_mock.development.js +0.73% 17.62 kB 17.75 kB +0.92% 4.23 kB 4.27 kB
oss-stable/scheduler/cjs/scheduler-unstable_mock.development.js +0.73% 17.62 kB 17.75 kB +0.92% 4.23 kB 4.27 kB
facebook-react-native/scheduler/cjs/SchedulerMock-dev.js +0.72% 17.67 kB 17.80 kB +0.87% 4.23 kB 4.27 kB
oss-experimental/scheduler/umd/scheduler-unstable_mock.development.js +0.70% 18.95 kB 19.08 kB +0.85% 4.35 kB 4.38 kB
oss-stable-semver/scheduler/umd/scheduler-unstable_mock.development.js +0.70% 18.95 kB 19.08 kB +0.85% 4.35 kB 4.38 kB
oss-stable/scheduler/umd/scheduler-unstable_mock.development.js +0.70% 18.95 kB 19.08 kB +0.85% 4.35 kB 4.38 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.development.js +0.62% 676.63 kB 680.80 kB +0.62% 146.47 kB 147.37 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.development.js +0.62% 676.65 kB 680.82 kB +0.62% 146.49 kB 147.39 kB
oss-stable-semver/react-test-renderer/umd/react-test-renderer.development.js +0.61% 708.82 kB 713.17 kB +0.60% 148.10 kB 148.99 kB
oss-stable/react-test-renderer/umd/react-test-renderer.development.js +0.61% 708.84 kB 713.19 kB +0.60% 148.12 kB 149.01 kB
oss-stable-semver/react-art/cjs/react-art.development.js +0.59% 703.77 kB 707.94 kB +0.59% 151.38 kB 152.27 kB
oss-stable/react-art/cjs/react-art.development.js +0.59% 703.80 kB 707.96 kB +0.59% 151.40 kB 152.29 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.development.js +0.59% 705.48 kB 709.64 kB +0.59% 152.13 kB 153.03 kB
oss-experimental/react-test-renderer/umd/react-test-renderer.development.js +0.59% 739.10 kB 743.45 kB +0.60% 153.79 kB 154.71 kB
facebook-react-native/react-test-renderer/cjs/ReactTestRenderer-dev.js +0.58% 719.88 kB 724.06 kB +0.59% 153.73 kB 154.64 kB
oss-experimental/react-art/cjs/react-art.development.js +0.57% 734.43 kB 738.60 kB +0.60% 157.57 kB 158.52 kB
facebook-www/ReactTestRenderer-dev.modern.js +0.56% 742.06 kB 746.24 kB +0.58% 157.83 kB 158.74 kB
facebook-www/ReactTestRenderer-dev.classic.js +0.56% 742.06 kB 746.24 kB +0.58% 157.83 kB 158.74 kB
facebook-www/SchedulerMock-dev.classic.js +0.56% 22.87 kB 22.99 kB +0.65% 5.41 kB 5.45 kB
facebook-www/SchedulerMock-dev.modern.js +0.56% 22.87 kB 22.99 kB +0.65% 5.41 kB 5.45 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler.development.js +0.54% 770.27 kB 774.44 kB +0.55% 163.40 kB 164.31 kB
oss-stable/react-reconciler/cjs/react-reconciler.development.js +0.54% 770.30 kB 774.47 kB +0.55% 163.43 kB 164.33 kB
oss-stable-semver/react-art/umd/react-art.development.js +0.54% 808.14 kB 812.49 kB +0.55% 169.52 kB 170.46 kB
oss-stable/react-art/umd/react-art.development.js +0.54% 808.16 kB 812.51 kB +0.55% 169.54 kB 170.48 kB
react-native/implementations/ReactFabric-dev.js +0.52% 797.24 kB 801.42 kB +0.52% 172.35 kB 173.24 kB
oss-experimental/react-reconciler/cjs/react-reconciler.development.js +0.52% 801.21 kB 805.38 kB +0.58% 169.56 kB 170.53 kB
react-native/implementations/ReactNativeRenderer-dev.js +0.52% 806.90 kB 811.09 kB +0.51% 174.96 kB 175.86 kB
oss-experimental/react-art/umd/react-art.development.js +0.52% 840.27 kB 844.62 kB +0.55% 175.72 kB 176.69 kB
facebook-www/ReactART-dev.modern.js +0.51% 818.74 kB 822.92 kB +0.52% 172.48 kB 173.38 kB
facebook-www/ReactART-dev.classic.js +0.50% 828.96 kB 833.15 kB +0.51% 174.61 kB 175.51 kB
react-native/implementations/ReactFabric-dev.fb.js +0.50% 834.36 kB 838.54 kB +0.50% 179.07 kB 179.97 kB
react-native/implementations/ReactNativeRenderer-dev.fb.js +0.50% 844.00 kB 848.18 kB +0.50% 181.71 kB 182.63 kB
oss-stable-semver/react-art/cjs/react-art.production.min.js +0.45% 83.89 kB 84.26 kB +0.59% 26.05 kB 26.20 kB
oss-stable/react-art/cjs/react-art.production.min.js +0.45% 83.91 kB 84.29 kB +0.58% 26.05 kB 26.20 kB
react-native/implementations/ReactFabric-profiling.js +0.44% 324.27 kB 325.68 kB +0.37% 57.24 kB 57.46 kB
react-native/implementations/ReactNativeRenderer-profiling.js +0.43% 331.13 kB 332.54 kB +0.38% 58.29 kB 58.52 kB
react-native/implementations/ReactFabric-profiling.fb.js +0.42% 340.63 kB 342.06 kB +0.38% 59.94 kB 60.17 kB
oss-experimental/react-art/cjs/react-art.production.min.js +0.42% 88.99 kB 89.36 kB +0.29% 27.52 kB 27.60 kB
react-native/implementations/ReactNativeRenderer-profiling.fb.js +0.41% 347.48 kB 348.91 kB +0.40% 60.96 kB 61.21 kB
oss-experimental/scheduler/cjs/scheduler.development.js +0.41% 17.59 kB 17.66 kB +0.38% 4.96 kB 4.98 kB
oss-stable-semver/scheduler/cjs/scheduler.development.js +0.41% 17.59 kB 17.66 kB +0.38% 4.96 kB 4.98 kB
oss-stable/scheduler/cjs/scheduler.development.js +0.41% 17.59 kB 17.66 kB +0.38% 4.96 kB 4.98 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.production.min.js +0.41% 91.16 kB 91.53 kB +0.53% 28.13 kB 28.28 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.production.min.js +0.41% 91.18 kB 91.56 kB +0.53% 28.13 kB 28.28 kB
facebook-react-native/scheduler/cjs/Scheduler-dev.js +0.41% 17.61 kB 17.69 kB +0.36% 4.97 kB 4.99 kB
oss-stable-semver/react-test-renderer/umd/react-test-renderer.production.min.js +0.40% 91.41 kB 91.78 kB +0.50% 28.54 kB 28.68 kB
oss-stable/react-test-renderer/umd/react-test-renderer.production.min.js +0.40% 91.43 kB 91.80 kB +0.49% 28.54 kB 28.68 kB
oss-stable-semver/react-dom/cjs/react-dom.development.js +0.40% 1,050.53 kB 1,054.71 kB +0.39% 234.57 kB 235.48 kB
oss-stable/react-dom/cjs/react-dom.development.js +0.40% 1,050.56 kB 1,054.73 kB +0.39% 234.60 kB 235.50 kB
oss-stable-semver/react-dom/umd/react-dom.development.js +0.40% 1,101.99 kB 1,106.35 kB +0.41% 237.15 kB 238.12 kB
oss-stable/react-dom/umd/react-dom.development.js +0.40% 1,102.02 kB 1,106.37 kB +0.41% 237.18 kB 238.15 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.production.min.js +0.39% 95.91 kB 96.28 kB +0.30% 29.52 kB 29.61 kB
oss-experimental/react-dom/cjs/react-dom-unstable_testing.development.js +0.39% 1,075.48 kB 1,079.65 kB +0.38% 239.98 kB 240.90 kB
react-native/implementations/ReactFabric-prod.js +0.39% 305.62 kB 306.80 kB +0.33% 54.14 kB 54.32 kB
oss-experimental/react-test-renderer/umd/react-test-renderer.production.min.js +0.39% 96.15 kB 96.52 kB +0.22% 29.89 kB 29.95 kB
oss-experimental/react-dom/cjs/react-dom.development.js +0.38% 1,083.44 kB 1,087.61 kB +0.40% 241.31 kB 242.27 kB
oss-experimental/react-dom/umd/react-dom.development.js +0.38% 1,136.48 kB 1,140.83 kB +0.41% 243.99 kB 244.98 kB
react-native/implementations/ReactNativeRenderer-prod.js +0.38% 312.43 kB 313.61 kB +0.34% 55.22 kB 55.41 kB
react-native/implementations/ReactFabric-prod.fb.js +0.38% 313.97 kB 315.15 kB +0.32% 55.76 kB 55.94 kB
facebook-www/ReactDOMTesting-dev.modern.js +0.37% 1,075.05 kB 1,079.02 kB +0.36% 239.56 kB 240.43 kB
react-native/implementations/ReactNativeRenderer-prod.fb.js +0.37% 320.77 kB 321.95 kB +0.31% 56.85 kB 57.03 kB
facebook-www/ReactART-prod.modern.js +0.36% 302.69 kB 303.79 kB +0.41% 52.04 kB 52.26 kB
facebook-www/ReactDOMTesting-dev.classic.js +0.36% 1,103.67 kB 1,107.64 kB +0.36% 245.37 kB 246.26 kB
facebook-www/ReactDOMForked-dev.modern.js +0.35% 1,190.32 kB 1,194.51 kB +0.37% 260.33 kB 261.30 kB
facebook-www/ReactDOM-dev.modern.js +0.35% 1,190.33 kB 1,194.51 kB +0.37% 260.34 kB 261.30 kB
facebook-www/ReactART-prod.classic.js +0.35% 313.52 kB 314.63 kB +0.36% 53.89 kB 54.08 kB
facebook-www/ReactDOMForked-dev.classic.js +0.34% 1,213.94 kB 1,218.13 kB +0.36% 264.82 kB 265.78 kB
facebook-www/ReactDOM-dev.classic.js +0.34% 1,213.94 kB 1,218.13 kB +0.36% 264.82 kB 265.78 kB
facebook-react-native/react-test-renderer/cjs/ReactTestRenderer-profiling.js +0.33% 292.91 kB 293.88 kB +0.45% 51.47 kB 51.70 kB
facebook-react-native/react-test-renderer/cjs/ReactTestRenderer-prod.js +0.33% 277.74 kB 278.66 kB +0.34% 49.20 kB 49.37 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler.profiling.min.js +0.31% 104.05 kB 104.37 kB +0.13% 31.47 kB 31.51 kB
oss-stable/react-reconciler/cjs/react-reconciler.profiling.min.js +0.31% 104.07 kB 104.39 kB +0.13% 31.49 kB 31.53 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler.production.min.js +0.31% 95.19 kB 95.49 kB +0.29% 29.22 kB 29.30 kB
oss-stable/react-reconciler/cjs/react-reconciler.production.min.js +0.31% 95.22 kB 95.51 kB +0.28% 29.24 kB 29.32 kB
oss-stable-semver/react-art/umd/react-art.production.min.js +0.31% 119.76 kB 120.13 kB +0.28% 37.29 kB 37.40 kB
oss-stable/react-art/umd/react-art.production.min.js +0.31% 119.79 kB 120.16 kB +0.28% 37.29 kB 37.40 kB
oss-experimental/react-art/umd/react-art.production.min.js +0.30% 124.83 kB 125.20 kB +0.19% 38.73 kB 38.80 kB
oss-experimental/react-reconciler/cjs/react-reconciler.production.min.js +0.29% 100.37 kB 100.67 kB +0.01% 30.73 kB 30.74 kB
facebook-www/Scheduler-dev.classic.js +0.29% 24.76 kB 24.83 kB +0.15% 6.66 kB 6.67 kB
facebook-www/Scheduler-dev.modern.js +0.29% 24.76 kB 24.83 kB +0.15% 6.66 kB 6.67 kB
oss-experimental/react-reconciler/cjs/react-reconciler.profiling.min.js +0.28% 109.25 kB 109.56 kB = 32.93 kB 32.88 kB
oss-stable-semver/react-dom/cjs/react-dom.production.min.js +0.22% 134.00 kB 134.30 kB = 42.92 kB 42.92 kB
oss-stable/react-dom/cjs/react-dom.production.min.js +0.22% 134.02 kB 134.32 kB = 42.92 kB 42.92 kB
oss-stable-semver/react-dom/cjs/react-dom.profiling.min.js +0.22% 143.50 kB 143.81 kB +0.04% 45.41 kB 45.43 kB
oss-stable/react-dom/cjs/react-dom.profiling.min.js +0.22% 143.52 kB 143.84 kB +0.04% 45.41 kB 45.43 kB
oss-stable-semver/react-dom/umd/react-dom.profiling.min.js +0.22% 142.95 kB 143.26 kB +0.08% 45.89 kB 45.92 kB
oss-stable/react-dom/umd/react-dom.profiling.min.js +0.22% 142.97 kB 143.28 kB +0.08% 45.88 kB 45.92 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js +0.21% 139.58 kB 139.88 kB +0.03% 44.54 kB 44.55 kB
oss-stable-semver/react-dom/umd/react-dom.production.min.js +0.21% 134.11 kB 134.39 kB +0.11% 43.62 kB 43.67 kB
oss-stable/react-dom/umd/react-dom.production.min.js +0.21% 134.14 kB 134.42 kB +0.11% 43.62 kB 43.67 kB
oss-experimental/react-dom/cjs/react-dom.profiling.min.js +0.21% 149.09 kB 149.40 kB = 46.96 kB 46.89 kB
oss-experimental/react-dom/umd/react-dom.profiling.min.js +0.21% 148.47 kB 148.78 kB +0.08% 47.42 kB 47.46 kB
oss-experimental/react-dom/cjs/react-dom-unstable_testing.production.min.js +0.21% 144.84 kB 145.14 kB = 46.59 kB 46.59 kB
oss-experimental/react-dom/umd/react-dom.production.min.js +0.20% 139.63 kB 139.91 kB = 45.20 kB 45.20 kB
oss-experimental/scheduler/cjs/scheduler.production.min.js = 4.29 kB 4.26 kB = 1.79 kB 1.79 kB
oss-stable-semver/scheduler/cjs/scheduler.production.min.js = 4.29 kB 4.26 kB = 1.79 kB 1.79 kB
oss-stable/scheduler/cjs/scheduler.production.min.js = 4.29 kB 4.26 kB = 1.79 kB 1.79 kB
facebook-www/SchedulerPostTask-prod.classic.js = 4.14 kB 4.07 kB = 1.14 kB 1.12 kB
facebook-www/SchedulerPostTask-prod.modern.js = 4.14 kB 4.07 kB = 1.14 kB 1.12 kB
facebook-www/SchedulerPostTask-profiling.classic.js = 4.14 kB 4.07 kB = 1.14 kB 1.12 kB
facebook-www/SchedulerPostTask-profiling.modern.js = 4.14 kB 4.07 kB = 1.14 kB 1.12 kB
facebook-www/SchedulerPostTask-dev.classic.js = 7.17 kB 7.01 kB = 2.14 kB 2.11 kB
facebook-www/SchedulerPostTask-dev.modern.js = 7.17 kB 7.01 kB = 2.14 kB 2.11 kB
oss-experimental/scheduler/cjs/scheduler-unstable_post_task.development.js = 7.04 kB 6.88 kB = 2.12 kB 2.08 kB
oss-stable-semver/scheduler/cjs/scheduler-unstable_post_task.development.js = 7.04 kB 6.88 kB = 2.12 kB 2.08 kB
oss-stable/scheduler/cjs/scheduler-unstable_post_task.development.js = 7.04 kB 6.88 kB = 2.12 kB 2.08 kB
oss-experimental/scheduler/cjs/scheduler-unstable_post_task.production.min.js = 2.02 kB 1.97 kB = 0.86 kB 0.85 kB
oss-stable-semver/scheduler/cjs/scheduler-unstable_post_task.production.min.js = 2.02 kB 1.97 kB = 0.86 kB 0.85 kB
oss-stable/scheduler/cjs/scheduler-unstable_post_task.production.min.js = 2.02 kB 1.97 kB = 0.86 kB 0.85 kB
oss-experimental/scheduler/umd/scheduler.development.js = 5.14 kB 4.90 kB = 0.88 kB 0.87 kB
oss-stable-semver/scheduler/umd/scheduler.development.js = 5.14 kB 4.90 kB = 0.88 kB 0.87 kB
oss-stable/scheduler/umd/scheduler.development.js = 5.14 kB 4.90 kB = 0.88 kB 0.87 kB
oss-experimental/scheduler/umd/scheduler.production.min.js = 4.88 kB 4.63 kB = 0.88 kB 0.87 kB
oss-experimental/scheduler/umd/scheduler.profiling.min.js = 4.88 kB 4.63 kB = 0.88 kB 0.87 kB
oss-stable-semver/scheduler/umd/scheduler.production.min.js = 4.88 kB 4.63 kB = 0.88 kB 0.87 kB
oss-stable-semver/scheduler/umd/scheduler.profiling.min.js = 4.88 kB 4.63 kB = 0.88 kB 0.87 kB
oss-stable/scheduler/umd/scheduler.production.min.js = 4.88 kB 4.63 kB = 0.88 kB 0.87 kB
oss-stable/scheduler/umd/scheduler.profiling.min.js = 4.88 kB 4.63 kB = 0.88 kB 0.87 kB

Generated by 🚫 dangerJS against eefdb73

When a fiber suspends, we should yield to the main thread in case the
data is already cached, to unblock a potential ping event.

By itself, this commit isn't useful because we don't do anything special
in the case where to do receive an immediate ping event. I've split this
out only to demonstrate that it doesn't break any existing behavior.

See the next commit for full context and motivation.
If a fiber suspends, and is pinged immediately in a microtask (or a
regular task that fires before React resumes rendering), try rendering
the same fiber again without unwinding the stack. This can be super
helpful when working with promises and async-await, because even if the
outermost promise hasn't been cached before, the underlying data may
have been preloaded. In many cases, we can continue rendering
immediately without having to show a fallback.

This optimization should work during any concurrent (time-sliced)
render. It doesn't work during discrete updates because those are
semantically required to finish synchronously — those get the current
behavior.
@acdlite acdlite marked this pull request as ready for review August 10, 2022 19:51
@acdlite acdlite requested a review from sebmarkbage August 10, 2022 19:51
@@ -195,10 +195,22 @@ function workLoop(hasTimeRemaining, initialTime) {
const continuationCallback = callback(didUserCallbackTimeout);
currentTime = getCurrentTime();
if (typeof continuationCallback === 'function') {
// If a continuation is returned, immediately yield to the main thread
Copy link
Collaborator

Choose a reason for hiding this comment

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

This heuristic is a bit weird because API wise it's only supposed to yield to higher priority but this makes it yield more to same/lower priority native events than before or if you don't use continuation.

It only makes sense if you assume that React only yields a continuation if it has already used up the allotted time because itself has a timer heuristic but other uses of continuations wouldn't necessarily do that.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

If there were a native way to schedule a continuation, I think how I'd implement it is: inside the reconciler, when exiting the work loop, choose between either native continuation (normal time slice case) or a separate postTask call (explicit yield to microtasks). If Scheduler tasks were 1:1 with native tasks that's how I would have done it in this PR, too — we should probably make that change, anyway, so that microtasks aren't blocked by successive tasks and it aligns more closely with postTask behavior. But that's a bigger change than I wanted to do within the scope of this PR. So for now I'm treating the continuation API as an internal React detail.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I suppose the previous solution (requestYield) is closer to that supposed end state, it's just slightly more overhead in the meantime if you assume that React is the only thing that uses this API.

@acdlite acdlite mentioned this pull request Aug 11, 2022
1 task
'Outer: 1',
'Suspend! [Async: 1]',
]);
expect(Scheduler).toFlushUntilNextPaint(['Loading...']);
Copy link
Member

Choose a reason for hiding this comment

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

Cool

@@ -963,33 +963,36 @@ describe('ReactSuspenseWithNoopRenderer', () => {

// @gate enableCache
it('resolves successfully even if fallback render is pending', async () => {
ReactNoop.render(
const root = ReactNoop.createRoot();
Copy link
Member

Choose a reason for hiding this comment

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

Is this new?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The createRoot method? No it's been there


// Wait for microtasks to resolve
// TODO: The async form of `act` should automatically yield to microtasks
// when a continuation is returned, the way Scheduler does.
Copy link
Member

Choose a reason for hiding this comment

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

+1, then we can get rid of all the other await nulls

expect(Scheduler).toFlushAndYield(['Continuation Task']);
});

it("toFlushAndYield keeps flushing even if there's a continuation", () => {
Copy link
Member

Choose a reason for hiding this comment

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

Why?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No inherent reason, just that there are a ton of tests that already use this helper. These internal flushing helpers are designed to test implementation details, and in nearly all of the existing uses, whether the work loop yields after a suspend is an irrelevant implementation detail.

The important API to get right is act, though I haven't implemented that in this PR yet.

@acdlite acdlite merged commit 8ef3a7c into facebook:main Aug 12, 2022
facebook-github-bot pushed a commit to facebook/react-native that referenced this pull request Sep 12, 2022
Summary:
This sync includes the following changes:
- **[c28f313e6](facebook/react@c28f313e6 )**: experimental_use(promise) for SSR ([#25214](facebook/react#25214)) //<Andrew Clark>//
- **[d6f9628a8](facebook/react@d6f9628a8 )**: Remove some RSC subset entry points that were removed in the main entry point ([#25209](facebook/react#25209)) //<Sebastian Markbåge>//
- **[a473d08fc](facebook/react@a473d08fc )**: Update to Flow from 0.97 to 0.122 ([#25204](facebook/react#25204)) //<Jan Kassens>//
- **[7028ce745](facebook/react@7028ce745 )**: experimental_use(promise) for Server Components ([#25207](facebook/react#25207)) //<Andrew Clark>//
- **[bfb65681e](facebook/react@bfb65681e )**: experimental_use(context)([#25202](facebook/react#25202)) //<mofeiZ>//
- **[f0efa1164](facebook/react@f0efa1164 )**: [flow] remove custom suppress comment config ([#25170](facebook/react#25170)) //<Jan Kassens>//
- **[2e7f422fe](facebook/react@2e7f422fe )**: Refactor: its type is Container ([#25153](facebook/react#25153)) //<bubucuo>//
- **[2c2d9a1df](facebook/react@2c2d9a1df )**: [eslint-plugin-react-hooks] only allow capitalized component names ([#25162](facebook/react#25162)) //<Jan Kassens>//
- **[36c908a6c](facebook/react@36c908a6c )**: Don't use the Flight terminology in public error messages ([#25166](facebook/react#25166)) //<Sebastian Markbåge>//
- **[8d1b057ec](facebook/react@8d1b057ec )**: [Flight] Minor error handling fixes ([#25151](facebook/react#25151)) //<Sebastian Markbåge>//
- **[9ff738f53](facebook/react@9ff738f53 )**: [devtools][easy] Fix flow type ([#25147](facebook/react#25147)) //<Tianyu Yao>//
- **[0de3ddf56](facebook/react@0de3ddf56 )**: Remove Symbol Polyfill (again) ([#25144](facebook/react#25144)) //<Ricky>//
- **[b36f72235](facebook/react@b36f72235 )**: Remove ReactFiberFlags MountLayoutDev and MountPassiveDev ([#25091](facebook/react#25091)) //<Samuel Susla>//
- **[b6978bc38](facebook/react@b6978bc38 )**: experimental_use(promise) ([#25084](facebook/react#25084)) //<Andrew Clark>//
- **[11ed7010c](facebook/react@11ed7010c )**: [Transition Tracing] onMarkerIncomplete - Tracing Marker/Suspense Boundary Deletions ([#24885](facebook/react#24885)) //<Luna Ruan>//
- **[b79894259](facebook/react@b79894259 )**: [Flight] Add support for Webpack Async Modules ([#25138](facebook/react#25138)) //<Sebastian Markbåge>//
- **[c8b778b7f](facebook/react@c8b778b7f )**: Fix typo: supportsMicrotask -> supportsMicrotasks ([#25142](facebook/react#25142)) //<kwzr>//
- **[d0f396651](facebook/react@d0f396651 )**: Allow functions to be used as module references ([#25137](facebook/react#25137)) //<Sebastian Markbåge>//
- **[38c5d8a03](facebook/react@38c5d8a03 )**: Test the node-register hooks in unit tests ([#25132](facebook/react#25132)) //<Sebastian Markbåge>//
- **[3f70e68ce](facebook/react@3f70e68ce )**: Return closestInstance in `getInspectorDataForViewAtPoint` ([#25118](facebook/react#25118)) //<Tianyu Yao>//
- **[3d443cad7](facebook/react@3d443cad7 )**: Update fixtures/flight to webpack 5 ([#25115](facebook/react#25115)) //<Tim Neutkens>//
- **[5d1ce6513](facebook/react@5d1ce6513 )**: Align StrictMode behaviour with production ([#25049](facebook/react#25049)) //<Samuel Susla>//
- **[9e67e7a31](facebook/react@9e67e7a31 )**: Scaffolding for useMemoCache hook ([#25123](facebook/react#25123)) //<Joseph Savona>//
- **[19e9a4c68](facebook/react@19e9a4c68 )**: Add missing createServerContext for experimental shared subset ([#25114](facebook/react#25114)) //<Jiachi Liu>//
- **[6ef466c68](facebook/react@6ef466c68 )**: make preamble and postamble types explicit and fix typo ([#25102](facebook/react#25102)) //<Josh Story>//
- **[796d31809](facebook/react@796d31809 )**: Implement basic stylesheet Resources for react-dom ([#25060](facebook/react#25060)) //<Josh Story>//
- **[32baab38f](facebook/react@32baab38f )**: [Transition Tracing] Add Tag Field to Marker Instance ([#25085](facebook/react#25085)) //<Luna Ruan>//
- **[8ef3a7c08](facebook/react@8ef3a7c08 )**: Resume immediately pinged fiber without unwinding ([#25074](facebook/react#25074)) //<Andrew Clark>//
- **[7bcc68772](facebook/react@7bcc68772 )**: Remove argument committedLanes from reappearLayoutEffects and recursivelyTraverseReappearLayoutEffects ([#25080](facebook/react#25080)) //<Samuel Susla>//
- **[ca990e9a7](facebook/react@ca990e9a7 )**: Add API to force Scheduler to yield for macrotask ([#25044](facebook/react#25044)) //<Andrew Clark>//
- **[b4204ede6](facebook/react@b4204ede6 )**: Clean up unused Deletion flag ([#24992](facebook/react#24992)) //<Andrew Clark>//
- **[e193be87e](facebook/react@e193be87e )**: [Transition Tracing] Add Offscreen Test ([#25035](facebook/react#25035)) //<Luna Ruan>//
- **[9fcaf88d5](facebook/react@9fcaf88d5 )**: Remove rootContainerInstance from unnecessary places ([#25024](facebook/react#25024)) //<Sebastian Markbåge>//
- **[80f3d8819](facebook/react@80f3d8819 )**: Mount/unmount passive effects when Offscreen visibility changes ([#24977](facebook/react#24977)) //<Andrew Clark>//

Changelog:
[General][Changed] - React Native sync for revisions 4ea064e...c28f313

Reviewed By: rickhanlonii

Differential Revision: D39384898

fbshipit-source-id: 20b080a53851d6dd9d522c8468dd02aab9ba76db
rickhanlonii pushed a commit that referenced this pull request Oct 6, 2022
* Yield to main thread if continuation is returned

Instead of using an imperative method `requestYield` to ask Scheduler to
yield to the main thread, we can assume that any time a Scheduler task
returns a continuation callback, it's because it wants to yield to the
main thread. We can assume the task already checked some condition that
caused it to return a continuation, so we don't need to do any
additional checks — we can immediately yield and schedule a new task
for the continuation.

The replaces the `requestYield` API that I added in ca990e9.

* Move unwind after error into main work loop

I need to be able to yield to the main thread in between when an error
is thrown and when the stack is unwound. (This is the motivation behind
the refactor, but it isn't implemented in this commit.) Currently the
unwind is inlined directly into `handleError`.

Instead, I've moved the unwind logic into the main work loop. At the
very beginning of the function, we check to see if the work-in-progress
is in a "suspended" state — that is, whether it needs to be unwound. If
it is, we will enter the unwind phase instead of the begin phase.

We only need to perform this check when we first enter the work loop:
at the beginning of a Scheduler chunk, or after something throws. We
don't need to perform it after every unit of work.

* Yield to main thread whenever a fiber suspends

When a fiber suspends, we should yield to the main thread in case the
data is already cached, to unblock a potential ping event.

By itself, this commit isn't useful because we don't do anything special
in the case where to do receive an immediate ping event. I've split this
out only to demonstrate that it doesn't break any existing behavior.

See the next commit for full context and motivation.

* Resume immediately pinged fiber without unwinding

If a fiber suspends, and is pinged immediately in a microtask (or a
regular task that fires before React resumes rendering), try rendering
the same fiber again without unwinding the stack. This can be super
helpful when working with promises and async-await, because even if the
outermost promise hasn't been cached before, the underlying data may
have been preloaded. In many cases, we can continue rendering
immediately without having to show a fallback.

This optimization should work during any concurrent (time-sliced)
render. It doesn't work during discrete updates because those are
semantically required to finish synchronously — those get the current
behavior.
OlimpiaZurek pushed a commit to OlimpiaZurek/react-native that referenced this pull request May 22, 2023
Summary:
This sync includes the following changes:
- **[c28f313e6](facebook/react@c28f313e6 )**: experimental_use(promise) for SSR ([facebook#25214](facebook/react#25214)) //<Andrew Clark>//
- **[d6f9628a8](facebook/react@d6f9628a8 )**: Remove some RSC subset entry points that were removed in the main entry point ([facebook#25209](facebook/react#25209)) //<Sebastian Markbåge>//
- **[a473d08fc](facebook/react@a473d08fc )**: Update to Flow from 0.97 to 0.122 ([facebook#25204](facebook/react#25204)) //<Jan Kassens>//
- **[7028ce745](facebook/react@7028ce745 )**: experimental_use(promise) for Server Components ([facebook#25207](facebook/react#25207)) //<Andrew Clark>//
- **[bfb65681e](facebook/react@bfb65681e )**: experimental_use(context)([facebook#25202](facebook/react#25202)) //<mofeiZ>//
- **[f0efa1164](facebook/react@f0efa1164 )**: [flow] remove custom suppress comment config ([facebook#25170](facebook/react#25170)) //<Jan Kassens>//
- **[2e7f422fe](facebook/react@2e7f422fe )**: Refactor: its type is Container ([facebook#25153](facebook/react#25153)) //<bubucuo>//
- **[2c2d9a1df](facebook/react@2c2d9a1df )**: [eslint-plugin-react-hooks] only allow capitalized component names ([facebook#25162](facebook/react#25162)) //<Jan Kassens>//
- **[36c908a6c](facebook/react@36c908a6c )**: Don't use the Flight terminology in public error messages ([facebook#25166](facebook/react#25166)) //<Sebastian Markbåge>//
- **[8d1b057ec](facebook/react@8d1b057ec )**: [Flight] Minor error handling fixes ([facebook#25151](facebook/react#25151)) //<Sebastian Markbåge>//
- **[9ff738f53](facebook/react@9ff738f53 )**: [devtools][easy] Fix flow type ([facebook#25147](facebook/react#25147)) //<Tianyu Yao>//
- **[0de3ddf56](facebook/react@0de3ddf56 )**: Remove Symbol Polyfill (again) ([facebook#25144](facebook/react#25144)) //<Ricky>//
- **[b36f72235](facebook/react@b36f72235 )**: Remove ReactFiberFlags MountLayoutDev and MountPassiveDev ([facebook#25091](facebook/react#25091)) //<Samuel Susla>//
- **[b6978bc38](facebook/react@b6978bc38 )**: experimental_use(promise) ([facebook#25084](facebook/react#25084)) //<Andrew Clark>//
- **[11ed7010c](facebook/react@11ed7010c )**: [Transition Tracing] onMarkerIncomplete - Tracing Marker/Suspense Boundary Deletions ([facebook#24885](facebook/react#24885)) //<Luna Ruan>//
- **[b79894259](facebook/react@b79894259 )**: [Flight] Add support for Webpack Async Modules ([facebook#25138](facebook/react#25138)) //<Sebastian Markbåge>//
- **[c8b778b7f](facebook/react@c8b778b7f )**: Fix typo: supportsMicrotask -> supportsMicrotasks ([facebook#25142](facebook/react#25142)) //<kwzr>//
- **[d0f396651](facebook/react@d0f396651 )**: Allow functions to be used as module references ([facebook#25137](facebook/react#25137)) //<Sebastian Markbåge>//
- **[38c5d8a03](facebook/react@38c5d8a03 )**: Test the node-register hooks in unit tests ([facebook#25132](facebook/react#25132)) //<Sebastian Markbåge>//
- **[3f70e68ce](facebook/react@3f70e68ce )**: Return closestInstance in `getInspectorDataForViewAtPoint` ([facebook#25118](facebook/react#25118)) //<Tianyu Yao>//
- **[3d443cad7](facebook/react@3d443cad7 )**: Update fixtures/flight to webpack 5 ([facebook#25115](facebook/react#25115)) //<Tim Neutkens>//
- **[5d1ce6513](facebook/react@5d1ce6513 )**: Align StrictMode behaviour with production ([facebook#25049](facebook/react#25049)) //<Samuel Susla>//
- **[9e67e7a31](facebook/react@9e67e7a31 )**: Scaffolding for useMemoCache hook ([facebook#25123](facebook/react#25123)) //<Joseph Savona>//
- **[19e9a4c68](facebook/react@19e9a4c68 )**: Add missing createServerContext for experimental shared subset ([facebook#25114](facebook/react#25114)) //<Jiachi Liu>//
- **[6ef466c68](facebook/react@6ef466c68 )**: make preamble and postamble types explicit and fix typo ([facebook#25102](facebook/react#25102)) //<Josh Story>//
- **[796d31809](facebook/react@796d31809 )**: Implement basic stylesheet Resources for react-dom ([facebook#25060](facebook/react#25060)) //<Josh Story>//
- **[32baab38f](facebook/react@32baab38f )**: [Transition Tracing] Add Tag Field to Marker Instance ([facebook#25085](facebook/react#25085)) //<Luna Ruan>//
- **[8ef3a7c08](facebook/react@8ef3a7c08 )**: Resume immediately pinged fiber without unwinding ([facebook#25074](facebook/react#25074)) //<Andrew Clark>//
- **[7bcc68772](facebook/react@7bcc68772 )**: Remove argument committedLanes from reappearLayoutEffects and recursivelyTraverseReappearLayoutEffects ([facebook#25080](facebook/react#25080)) //<Samuel Susla>//
- **[ca990e9a7](facebook/react@ca990e9a7 )**: Add API to force Scheduler to yield for macrotask ([facebook#25044](facebook/react#25044)) //<Andrew Clark>//
- **[b4204ede6](facebook/react@b4204ede6 )**: Clean up unused Deletion flag ([facebook#24992](facebook/react#24992)) //<Andrew Clark>//
- **[e193be87e](facebook/react@e193be87e )**: [Transition Tracing] Add Offscreen Test ([facebook#25035](facebook/react#25035)) //<Luna Ruan>//
- **[9fcaf88d5](facebook/react@9fcaf88d5 )**: Remove rootContainerInstance from unnecessary places ([facebook#25024](facebook/react#25024)) //<Sebastian Markbåge>//
- **[80f3d8819](facebook/react@80f3d8819 )**: Mount/unmount passive effects when Offscreen visibility changes ([facebook#24977](facebook/react#24977)) //<Andrew Clark>//

Changelog:
[General][Changed] - React Native sync for revisions 4ea064e...c28f313

Reviewed By: rickhanlonii

Differential Revision: D39384898

fbshipit-source-id: 20b080a53851d6dd9d522c8468dd02aab9ba76db
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.

5 participants