diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js index 2e8b443d22ca..8e75135ff6e0 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js @@ -41,6 +41,7 @@ import { enableSiblingPrerendering, enableComponentPerformanceTrack, enableYieldingBeforePassive, + enableThrottledScheduling, } from 'shared/ReactFeatureFlags'; import ReactSharedInternals from 'shared/ReactSharedInternals'; import is from 'shared/objectIs'; @@ -2610,8 +2611,10 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) { // can't trust the result of `shouldYield`, because the host I/O is // likely mocked. workLoopSync(); + } else if (enableThrottledScheduling) { + workLoopConcurrent(includesNonIdleWork(lanes)); } else { - workLoopConcurrent(); + workLoopConcurrentByScheduler(); } break; } catch (thrownValue) { @@ -2650,10 +2653,27 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) { } /** @noinline */ -function workLoopConcurrent() { +function workLoopConcurrent(nonIdle: boolean) { + // We yield every other "frame" when rendering Transition or Retries. Those are blocking + // revealing new content. The purpose of this yield is not to avoid the overhead of yielding, + // which is very low, but rather to intentionally block any frequently occuring other main + // thread work like animations from starving our work. In other words, the purpose of this + // is to reduce the framerate of animations to 30 frames per second. + // For Idle work we yield every 5ms to keep animations going smooth. + if (workInProgress !== null) { + const yieldAfter = now() + (nonIdle ? 25 : 5); + do { + // $FlowFixMe[incompatible-call] flow doesn't know that now() is side-effect free + performUnitOfWork(workInProgress); + } while (workInProgress !== null && now() < yieldAfter); + } +} + +/** @noinline */ +function workLoopConcurrentByScheduler() { // Perform work until Scheduler asks us to yield while (workInProgress !== null && !shouldYield()) { - // $FlowFixMe[incompatible-call] found when upgrading Flow + // $FlowFixMe[incompatible-call] flow doesn't know that shouldYield() is side-effect free performUnitOfWork(workInProgress); } } diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index 0720ab2a88b7..f1332a2bfd1e 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -81,6 +81,9 @@ export const enableLegacyFBSupport = false; // Fix gated tests that fail with this flag enabled before turning it back on. export const enableYieldingBeforePassive = false; +// Experiment to intentionally yield less to block high framerate animations. +export const enableThrottledScheduling = false; + export const enableLegacyCache = __EXPERIMENTAL__; export const enableAsyncIterableChildren = __EXPERIMENTAL__; diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 4ea9499e6ca8..ed06661eedc3 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -81,6 +81,7 @@ export const syncLaneExpirationMs = 250; export const transitionLaneExpirationMs = 5000; export const enableHydrationLaneScheduling = true; export const enableYieldingBeforePassive = false; +export const enableThrottledScheduling = false; // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index dddd80aeea82..2220891b7b42 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -73,6 +73,8 @@ export const enableHydrationLaneScheduling = true; export const enableYieldingBeforePassive = false; +export const enableThrottledScheduling = false; + // Profiling Only export const enableProfilerTimer = __PROFILE__; export const enableProfilerCommitHooks = __PROFILE__; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index 95826007dc9c..2c5bc8f297ba 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -72,6 +72,8 @@ export const enableUseResourceEffectHook = false; export const enableYieldingBeforePassive = true; +export const enableThrottledScheduling = false; + // TODO: This must be in sync with the main ReactFeatureFlags file because // the Test Renderer's value must be the same as the one used by the // react package. diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js index 81060cfafb1b..1c2fbb484796 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js @@ -69,6 +69,7 @@ export const enableSiblingPrerendering = true; export const enableUseResourceEffectHook = true; export const enableHydrationLaneScheduling = true; export const enableYieldingBeforePassive = false; +export const enableThrottledScheduling = false; // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index e0e9906d52b8..8e96038f4424 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -84,5 +84,7 @@ export const enableHydrationLaneScheduling = true; export const enableYieldingBeforePassive = false; +export const enableThrottledScheduling = false; + // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index cf514f93d43a..658443aea13e 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -58,6 +58,8 @@ export const enableLegacyFBSupport = true; export const enableYieldingBeforePassive = false; +export const enableThrottledScheduling = false; + export const enableHydrationLaneScheduling = true; export const enableComponentPerformanceTrack = false;