@samitouri / QOS-React-2 / commits / dbe98a5aae

Move sync task queue to its own module (#21109)

The sync task queue is React-specific and doesn't really have anything to do with Scheduler. We'd keep using it even once `postTask` exists. By separating that part out, `SchedulerWithReactIntegration` is now just a module that re-exports the Scheduler API. So I unforked it. When we switch to ES Modules, we can remove this re-exporting module.

Andrew Clark committed Mar 28, 2021 at 16:15 UTC dbe98a5aae79efce4de3eea95171d1ef70537fdb
10 files changed +84 -112
packages/react-dom/src/events/ReactDOMEventListener.js
+4 -12
@@ -34,25 +34,21 @@ import {
34 import getEventTarget from './getEventTarget';
35 import {getClosestInstanceFromNode} from '../client/ReactDOMComponentTree';
36
37 -import {
38 - enableLegacyFBSupport,
39 - enableNewReconciler,
40 -} from 'shared/ReactFeatureFlags';
37 +import {enableLegacyFBSupport} from 'shared/ReactFeatureFlags';
38 import {dispatchEventForPluginEventSystem} from './DOMPluginEventSystem';
39 import {
40 flushDiscreteUpdatesIfNeeded,
41 discreteUpdates,
42 } from './ReactDOMUpdateBatching';
43
47 -import {getCurrentPriorityLevel as getCurrentPriorityLevel_old} from 'react-reconciler/src/SchedulerWithReactIntegration.old';
44 import {
49 - getCurrentPriorityLevel as getCurrentPriorityLevel_new,
45 + getCurrentPriorityLevel as getCurrentSchedulerPriorityLevel,
46 IdlePriority as IdleSchedulerPriority,
47 ImmediatePriority as ImmediateSchedulerPriority,
48 LowPriority as LowSchedulerPriority,
49 NormalPriority as NormalSchedulerPriority,
50 UserBlockingPriority as UserBlockingSchedulerPriority,
55 -} from 'react-reconciler/src/SchedulerWithReactIntegration.new';
51 +} from 'react-reconciler/src/Scheduler';
52 import {
53 DiscreteEventPriority,
54 ContinuousEventPriority,
@@ -62,10 +58,6 @@ import {
58 setCurrentUpdatePriority,
59 } from 'react-reconciler/src/ReactEventPriorities';
60
65 -const getCurrentPriorityLevel = enableNewReconciler
66 - ? getCurrentPriorityLevel_new
67 - : getCurrentPriorityLevel_old;
68 -
61 // TODO: can we stop exporting these?
62 export let _enabled = true;
63
@@ -392,7 +384,7 @@ export function getEventPriority(domEventName: DOMEventName): * {
384 // We might be in the Scheduler callback.
385 // Eventually this mechanism will be replaced by a check
386 // of the current priority on the native scheduler.
395 - const schedulerPriority = getCurrentPriorityLevel();
387 + const schedulerPriority = getCurrentSchedulerPriorityLevel();
388 switch (schedulerPriority) {
389 case ImmediateSchedulerPriority:
390 return DiscreteEventPriority;
packages/react-reconciler/src/ReactFiberCompleteWork.new.js
+1 -1
@@ -28,7 +28,7 @@ import type {Cache, SpawnedCachePool} from './ReactFiberCacheComponent.new';
28
29 import {resetWorkInProgressVersions as resetMutableSourceWorkInProgressVersions} from './ReactMutableSource.new';
30
31 -import {now} from './SchedulerWithReactIntegration.new';
31 +import {now} from './Scheduler';
32
33 import {
34 IndeterminateComponent,
packages/react-reconciler/src/ReactFiberCompleteWork.old.js
+1 -1
@@ -28,7 +28,7 @@ import type {Cache, SpawnedCachePool} from './ReactFiberCacheComponent.old';
28
29 import {resetWorkInProgressVersions as resetMutableSourceWorkInProgressVersions} from './ReactMutableSource.old';
30
31 -import {now} from './SchedulerWithReactIntegration.old';
31 +import {now} from './Scheduler';
32
33 import {
34 IndeterminateComponent,
packages/react-reconciler/src/ReactFiberDevToolsHook.new.js
+1 -1
@@ -25,7 +25,7 @@ import {
25 UserBlockingPriority as UserBlockingSchedulerPriority,
26 NormalPriority as NormalSchedulerPriority,
27 IdlePriority as IdleSchedulerPriority,
28 -} from './SchedulerWithReactIntegration.new';
28 +} from './Scheduler';
29
30 declare var __REACT_DEVTOOLS_GLOBAL_HOOK__: Object | void;
31
packages/react-reconciler/src/ReactFiberDevToolsHook.old.js
+1 -1
@@ -25,7 +25,7 @@ import {
25 UserBlockingPriority as UserBlockingSchedulerPriority,
26 NormalPriority as NormalSchedulerPriority,
27 IdlePriority as IdleSchedulerPriority,
28 -} from './SchedulerWithReactIntegration.old';
28 +} from './Scheduler';
29
30 declare var __REACT_DEVTOOLS_GLOBAL_HOOK__: Object | void;
31
packages/react-reconciler/src/ReactFiberSyncTaskQueue.new.js new
+67
@@ -0,0 +1,67 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +import type {SchedulerCallback} from './Scheduler';
11 +
12 +import {
13 + DiscreteEventPriority,
14 + getCurrentUpdatePriority,
15 + setCurrentUpdatePriority,
16 +} from './ReactEventPriorities.new';
17 +import {ImmediatePriority, scheduleCallback} from './Scheduler';
18 +
19 +let syncQueue: Array<SchedulerCallback> | null = null;
20 +let isFlushingSyncQueue: boolean = false;
21 +
22 +export function scheduleSyncCallback(callback: SchedulerCallback) {
23 + // Push this callback into an internal queue. We'll flush these either in
24 + // the next tick, or earlier if something calls `flushSyncCallbackQueue`.
25 + if (syncQueue === null) {
26 + syncQueue = [callback];
27 + } else {
28 + // Push onto existing queue. Don't need to schedule a callback because
29 + // we already scheduled one when we created the queue.
30 + syncQueue.push(callback);
31 + }
32 +}
33 +
34 +export function flushSyncCallbackQueue() {
35 + if (!isFlushingSyncQueue && syncQueue !== null) {
36 + // Prevent re-entrancy.
37 + isFlushingSyncQueue = true;
38 + let i = 0;
39 + const previousUpdatePriority = getCurrentUpdatePriority();
40 + try {
41 + const isSync = true;
42 + const queue = syncQueue;
43 + // TODO: Is this necessary anymore? The only user code that runs in this
44 + // queue is in the render or commit phases.
45 + setCurrentUpdatePriority(DiscreteEventPriority);
46 + for (; i < queue.length; i++) {
47 + let callback = queue[i];
48 + do {
49 + callback = callback(isSync);
50 + } while (callback !== null);
51 + }
52 + syncQueue = null;
53 + } catch (error) {
54 + // If something throws, leave the remaining callbacks on the queue.
55 + if (syncQueue !== null) {
56 + syncQueue = syncQueue.slice(i + 1);
57 + }
58 + // Resume flushing in the next tick
59 + scheduleCallback(ImmediatePriority, flushSyncCallbackQueue);
60 + throw error;
61 + } finally {
62 + setCurrentUpdatePriority(previousUpdatePriority);
63 + isFlushingSyncQueue = false;
64 + }
65 + }
66 + return null;
67 +}
packages/react-reconciler/src/ReactFiberSyncTaskQueue.old.js renamed
+3 -38
@@ -7,50 +7,15 @@
7 * @flow
8 */
9
10 -// This module only exists as an ESM wrapper around the external CommonJS
11 -// Scheduler dependency. Notice that we're intentionally not using named imports
12 -// because Rollup would use dynamic dispatch for CommonJS interop named imports.
13 -// When we switch to ESM, we can delete this module.
14 -import * as Scheduler from 'scheduler';
15 -import {__interactionsRef} from 'scheduler/tracing';
16 -import {enableSchedulerTracing} from 'shared/ReactFeatureFlags';
17 -import invariant from 'shared/invariant';
10 +import type {SchedulerCallback} from './Scheduler';
11 +
12 import {
13 DiscreteEventPriority,
14 getCurrentUpdatePriority,
15 setCurrentUpdatePriority,
16 } from './ReactEventPriorities.old';
17 +import {ImmediatePriority, scheduleCallback} from './Scheduler';
18
24 -export const scheduleCallback = Scheduler.unstable_scheduleCallback;
25 -export const cancelCallback = Scheduler.unstable_cancelCallback;
26 -export const shouldYield = Scheduler.unstable_shouldYield;
27 -export const requestPaint = Scheduler.unstable_requestPaint;
28 -export const now = Scheduler.unstable_now;
29 -export const getCurrentPriorityLevel =
30 - Scheduler.unstable_getCurrentPriorityLevel;
31 -export const ImmediatePriority = Scheduler.unstable_ImmediatePriority;
32 -export const UserBlockingPriority = Scheduler.unstable_UserBlockingPriority;
33 -export const NormalPriority = Scheduler.unstable_NormalPriority;
34 -export const LowPriority = Scheduler.unstable_LowPriority;
35 -export const IdlePriority = Scheduler.unstable_IdlePriority;
36 -
37 -if (enableSchedulerTracing) {
38 - // Provide explicit error message when production+profiling bundle of e.g.
39 - // react-dom is used with production (non-profiling) bundle of
40 - // scheduler/tracing
41 - invariant(
42 - __interactionsRef != null && __interactionsRef.current != null,
43 - 'It is not supported to run the profiling version of a renderer (for ' +
44 - 'example, `react-dom/profiling`) without also replacing the ' +
45 - '`scheduler/tracing` module with `scheduler/tracing-profiling`. Your ' +
46 - 'bundler might have a setting for aliasing both modules. Learn more at ' +
47 - 'https://reactjs.org/link/profiling',
48 - );
49 -}
50 -
51 -export type SchedulerCallback = (isSync: boolean) => SchedulerCallback | null;
52 -
53 -// TODO: Move sync task queue to its own module.
19 let syncQueue: Array<SchedulerCallback> | null = null;
20 let isFlushingSyncQueue: boolean = false;
21
packages/react-reconciler/src/ReactFiberWorkLoop.new.js
+3 -1
@@ -46,9 +46,11 @@ import {
46 UserBlockingPriority as UserBlockingSchedulerPriority,
47 NormalPriority as NormalSchedulerPriority,
48 IdlePriority as IdleSchedulerPriority,
49 +} from './Scheduler';
50 +import {
51 flushSyncCallbackQueue,
52 scheduleSyncCallback,
51 -} from './SchedulerWithReactIntegration.new';
53 +} from './ReactFiberSyncTaskQueue.new';
54 import {
55 NoFlags as NoHookEffect,
56 Passive as HookPassive,
packages/react-reconciler/src/ReactFiberWorkLoop.old.js
+3 -1
@@ -46,9 +46,11 @@ import {
46 UserBlockingPriority as UserBlockingSchedulerPriority,
47 NormalPriority as NormalSchedulerPriority,
48 IdlePriority as IdleSchedulerPriority,
49 +} from './Scheduler';
50 +import {
51 flushSyncCallbackQueue,
52 scheduleSyncCallback,
51 -} from './SchedulerWithReactIntegration.old';
53 +} from './ReactFiberSyncTaskQueue.old';
54 import {
55 NoFlags as NoHookEffect,
56 Passive as HookPassive,
packages/react-reconciler/src/Scheduler.js renamed
-56
@@ -15,11 +15,6 @@ import * as Scheduler from 'scheduler';
15 import {__interactionsRef} from 'scheduler/tracing';
16 import {enableSchedulerTracing} from 'shared/ReactFeatureFlags';
17 import invariant from 'shared/invariant';
18 -import {
19 - DiscreteEventPriority,
20 - getCurrentUpdatePriority,
21 - setCurrentUpdatePriority,
22 -} from './ReactEventPriorities.new';
18
19 export const scheduleCallback = Scheduler.unstable_scheduleCallback;
20 export const cancelCallback = Scheduler.unstable_cancelCallback;
@@ -49,54 +44,3 @@ if (enableSchedulerTracing) {
44 }
45
46 export type SchedulerCallback = (isSync: boolean) => SchedulerCallback | null;
52 -
53 -// TODO: Move sync task queue to its own module.
54 -let syncQueue: Array<SchedulerCallback> | null = null;
55 -let isFlushingSyncQueue: boolean = false;
56 -
57 -export function scheduleSyncCallback(callback: SchedulerCallback) {
58 - // Push this callback into an internal queue. We'll flush these either in
59 - // the next tick, or earlier if something calls `flushSyncCallbackQueue`.
60 - if (syncQueue === null) {
61 - syncQueue = [callback];
62 - } else {
63 - // Push onto existing queue. Don't need to schedule a callback because
64 - // we already scheduled one when we created the queue.
65 - syncQueue.push(callback);
66 - }
67 -}
68 -
69 -export function flushSyncCallbackQueue() {
70 - if (!isFlushingSyncQueue && syncQueue !== null) {
71 - // Prevent re-entrancy.
72 - isFlushingSyncQueue = true;
73 - let i = 0;
74 - const previousUpdatePriority = getCurrentUpdatePriority();
75 - try {
76 - const isSync = true;
77 - const queue = syncQueue;
78 - // TODO: Is this necessary anymore? The only user code that runs in this
79 - // queue is in the render or commit phases.
80 - setCurrentUpdatePriority(DiscreteEventPriority);
81 - for (; i < queue.length; i++) {
82 - let callback = queue[i];
83 - do {
84 - callback = callback(isSync);
85 - } while (callback !== null);
86 - }
87 - syncQueue = null;
88 - } catch (error) {
89 - // If something throws, leave the remaining callbacks on the queue.
90 - if (syncQueue !== null) {
91 - syncQueue = syncQueue.slice(i + 1);
92 - }
93 - // Resume flushing in the next tick
94 - scheduleCallback(ImmediatePriority, flushSyncCallbackQueue);
95 - throw error;
96 - } finally {
97 - setCurrentUpdatePriority(previousUpdatePriority);
98 - isFlushingSyncQueue = false;
99 - }
100 - }
101 - return null;
102 -}