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

[scheduler] Yield many times per frame, no rAF #16214

Merged
merged 1 commit into from Jul 26, 2019

Conversation

@acdlite
Copy link
Member

@acdlite acdlite commented Jul 25, 2019

Adds experimental flag to yield many times per frame using a message event loop, instead of the current approach of guessing the next vsync and yielding at the end of the frame.

This new approach forgoes a requestAnimationFrame entirely. It posts a message event and performs a small amount of work (5ms) before yielding to the browser, regardless of where it might be in the vsync cycle. At the end of the event, if there's work left over, it posts another message event.

This should keep the main thread responsive even for really high frame rates. It also shouldn't matter if the hardware frame rate changes after page load (our current heuristic only detects if the frame rate increases, not decreases).

The main risk is that yielding more often will exacerbate main thread contention with other browser tasks.

I'm also not sure to what extent message events are throttled when the tab is backgrounded, relative to requestAnimationFrame or setTimeout. I'm starting with the assumption that message events fire with at least the same priority as timers, but I'll need to confirm.

Let's try it and see.

Adds experimental flag to yield many times per frame using a message
event loop, instead of the current approach of guessing the next vsync
and yielding at the end of the frame.

This new approach forgoes a `requestAnimationFrame` entirely. It posts a
message event and performs a small amount of work (5ms) before yielding
to the browser, regardless of where it might be in the vsync cycle. At
the end of the event, if there's work left over, it posts another
message event.

This should keep the main thread responsive even for really high frame
rates. It also shouldn't matter if the hardware frame rate changes after
page load (our current heuristic only detects if the frame rate
increases, not decreases).

The main risk is that yielding more often will exacerbate main thread
contention with other browser tasks.

Let's try it and see.
@sizebot
Copy link

@sizebot sizebot commented Jul 25, 2019

React: size: 0.0%, gzip: -0.0%

Details of bundled changes.

Comparing: c0830a0...5b706be

react

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react.development.js +1.6% +1.1% 115.09 KB 116.91 KB 29.01 KB 29.33 KB UMD_DEV
react.production.min.js 0.0% -0.0% 13.03 KB 13.03 KB 5.08 KB 5.07 KB UMD_PROD
react.profiling.min.js 0.0% 0.0% 15.23 KB 15.23 KB 5.61 KB 5.61 KB UMD_PROFILING
react.production.min.js 0.0% 0.0% 6.68 KB 6.68 KB 2.75 KB 2.75 KB NODE_PROD

scheduler

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
SchedulerMock-dev.js +0.4% +0.5% 21.28 KB 21.36 KB 4.64 KB 4.66 KB FB_WWW_DEV
scheduler.development.js +6.5% +4.4% 28.14 KB 29.96 KB 7.03 KB 7.35 KB NODE_DEV
Scheduler-dev.js +6.8% +4.6% 28.84 KB 30.79 KB 7.15 KB 7.48 KB FB_WWW_DEV
Scheduler-prod.js 🔺+4.4% 🔺+3.3% 17.17 KB 17.92 KB 3.59 KB 3.7 KB FB_WWW_PROD
scheduler-tracing.production.min.js 0.0% 🔺+0.3% 728 B 728 B 381 B 382 B NODE_PROD
scheduler-unstable_mock.development.js 0.0% 0.0% 20.8 KB 20.8 KB 4.56 KB 4.56 KB UMD_DEV
scheduler-tracing.profiling.min.js 0.0% +0.1% 3.26 KB 3.26 KB 998 B 999 B NODE_PROFILING
scheduler-unstable_mock.production.min.js 0.0% -0.1% 5.07 KB 5.07 KB 1.94 KB 1.94 KB NODE_PROD

Generated by 🚫 dangerJS

@acdlite acdlite force-pushed the acdlite:scheduler-message-loop branch 2 times, most recently from 7cc5b84 to 5b706be Jul 25, 2019
@acdlite acdlite merged commit 75ab53b into facebook:master Jul 26, 2019
12 checks passed
12 checks passed
ci/circleci: build Your tests passed on CircleCI!
Details
ci/circleci: flow Your tests passed on CircleCI!
Details
ci/circleci: lint Your tests passed on CircleCI!
Details
ci/circleci: lint_build Your tests passed on CircleCI!
Details
ci/circleci: process_artifacts Your tests passed on CircleCI!
Details
ci/circleci: setup Your tests passed on CircleCI!
Details
ci/circleci: test_build Your tests passed on CircleCI!
Details
ci/circleci: test_build_prod Your tests passed on CircleCI!
Details
ci/circleci: test_dom_fixtures Your tests passed on CircleCI!
Details
ci/circleci: test_source Your tests passed on CircleCI!
Details
ci/circleci: test_source_persistent Your tests passed on CircleCI!
Details
ci/circleci: test_source_prod Your tests passed on CircleCI!
Details
@yisar
Copy link
Contributor

@yisar yisar commented Oct 4, 2019

Why not use setTimeOut 0 directly? What the difference between setTimeOut and Message Channel?

This was referenced Oct 5, 2019
@SyMind
Copy link

@SyMind SyMind commented Oct 22, 2020

Promise polyfill was originally used in React, why not use Promise? Now queueMicroTask is living standard, why not use it?

@jddxf
Copy link
Contributor

@jddxf jddxf commented Nov 10, 2020

@SyMind Microtasks are continuously executed until the microtask queue on the event loop is empty which means a higher-pri task such as one for user interaction would be blocked by React.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

9 participants
You can’t perform that action at this time.