Skip to content

Commit ee9ff3e

Browse files
committed
Move sync task queue to its own module (facebook#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.
1 parent b09af77 commit ee9ff3e

10 files changed

+84
-112
lines changed

packages/react-dom/src/events/ReactDOMEventListener.js

+4-12
Original file line numberDiff line numberDiff line change
@@ -34,25 +34,21 @@ import {
3434
import getEventTarget from './getEventTarget';
3535
import {getClosestInstanceFromNode} from '../client/ReactDOMComponentTree';
3636

37-
import {
38-
enableLegacyFBSupport,
39-
enableNewReconciler,
40-
} from 'shared/ReactFeatureFlags';
37+
import {enableLegacyFBSupport} from 'shared/ReactFeatureFlags';
4138
import {dispatchEventForPluginEventSystem} from './DOMPluginEventSystem';
4239
import {
4340
flushDiscreteUpdatesIfNeeded,
4441
discreteUpdates,
4542
} from './ReactDOMUpdateBatching';
4643

47-
import {getCurrentPriorityLevel as getCurrentPriorityLevel_old} from 'react-reconciler/src/SchedulerWithReactIntegration.old';
4844
import {
49-
getCurrentPriorityLevel as getCurrentPriorityLevel_new,
45+
getCurrentPriorityLevel as getCurrentSchedulerPriorityLevel,
5046
IdlePriority as IdleSchedulerPriority,
5147
ImmediatePriority as ImmediateSchedulerPriority,
5248
LowPriority as LowSchedulerPriority,
5349
NormalPriority as NormalSchedulerPriority,
5450
UserBlockingPriority as UserBlockingSchedulerPriority,
55-
} from 'react-reconciler/src/SchedulerWithReactIntegration.new';
51+
} from 'react-reconciler/src/Scheduler';
5652
import {
5753
DiscreteEventPriority,
5854
ContinuousEventPriority,
@@ -62,10 +58,6 @@ import {
6258
setCurrentUpdatePriority,
6359
} from 'react-reconciler/src/ReactEventPriorities';
6460

65-
const getCurrentPriorityLevel = enableNewReconciler
66-
? getCurrentPriorityLevel_new
67-
: getCurrentPriorityLevel_old;
68-
6961
// TODO: can we stop exporting these?
7062
export let _enabled = true;
7163

@@ -392,7 +384,7 @@ export function getEventPriority(domEventName: DOMEventName): * {
392384
// We might be in the Scheduler callback.
393385
// Eventually this mechanism will be replaced by a check
394386
// of the current priority on the native scheduler.
395-
const schedulerPriority = getCurrentPriorityLevel();
387+
const schedulerPriority = getCurrentSchedulerPriorityLevel();
396388
switch (schedulerPriority) {
397389
case ImmediateSchedulerPriority:
398390
return DiscreteEventPriority;

packages/react-reconciler/src/ReactFiberCompleteWork.new.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import type {Cache, SpawnedCachePool} from './ReactFiberCacheComponent.new';
2828

2929
import {resetWorkInProgressVersions as resetMutableSourceWorkInProgressVersions} from './ReactMutableSource.new';
3030

31-
import {now} from './SchedulerWithReactIntegration.new';
31+
import {now} from './Scheduler';
3232

3333
import {
3434
IndeterminateComponent,

packages/react-reconciler/src/ReactFiberCompleteWork.old.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import type {Cache, SpawnedCachePool} from './ReactFiberCacheComponent.old';
2828

2929
import {resetWorkInProgressVersions as resetMutableSourceWorkInProgressVersions} from './ReactMutableSource.old';
3030

31-
import {now} from './SchedulerWithReactIntegration.old';
31+
import {now} from './Scheduler';
3232

3333
import {
3434
IndeterminateComponent,

packages/react-reconciler/src/ReactFiberDevToolsHook.new.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
UserBlockingPriority as UserBlockingSchedulerPriority,
2626
NormalPriority as NormalSchedulerPriority,
2727
IdlePriority as IdleSchedulerPriority,
28-
} from './SchedulerWithReactIntegration.new';
28+
} from './Scheduler';
2929

3030
declare var __REACT_DEVTOOLS_GLOBAL_HOOK__: Object | void;
3131

packages/react-reconciler/src/ReactFiberDevToolsHook.old.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
UserBlockingPriority as UserBlockingSchedulerPriority,
2626
NormalPriority as NormalSchedulerPriority,
2727
IdlePriority as IdleSchedulerPriority,
28-
} from './SchedulerWithReactIntegration.old';
28+
} from './Scheduler';
2929

3030
declare var __REACT_DEVTOOLS_GLOBAL_HOOK__: Object | void;
3131

Original file line numberDiff line numberDiff line change
@@ -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/SchedulerWithReactIntegration.old.js packages/react-reconciler/src/ReactFiberSyncTaskQueue.old.js

+3-38
Original file line numberDiff line numberDiff line change
@@ -7,50 +7,15 @@
77
* @flow
88
*/
99

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+
1812
import {
1913
DiscreteEventPriority,
2014
getCurrentUpdatePriority,
2115
setCurrentUpdatePriority,
2216
} from './ReactEventPriorities.old';
17+
import {ImmediatePriority, scheduleCallback} from './Scheduler';
2318

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.
5419
let syncQueue: Array<SchedulerCallback> | null = null;
5520
let isFlushingSyncQueue: boolean = false;
5621

packages/react-reconciler/src/ReactFiberWorkLoop.new.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,11 @@ import {
4646
UserBlockingPriority as UserBlockingSchedulerPriority,
4747
NormalPriority as NormalSchedulerPriority,
4848
IdlePriority as IdleSchedulerPriority,
49+
} from './Scheduler';
50+
import {
4951
flushSyncCallbackQueue,
5052
scheduleSyncCallback,
51-
} from './SchedulerWithReactIntegration.new';
53+
} from './ReactFiberSyncTaskQueue.new';
5254
import {
5355
NoFlags as NoHookEffect,
5456
Passive as HookPassive,

packages/react-reconciler/src/ReactFiberWorkLoop.old.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,11 @@ import {
4646
UserBlockingPriority as UserBlockingSchedulerPriority,
4747
NormalPriority as NormalSchedulerPriority,
4848
IdlePriority as IdleSchedulerPriority,
49+
} from './Scheduler';
50+
import {
4951
flushSyncCallbackQueue,
5052
scheduleSyncCallback,
51-
} from './SchedulerWithReactIntegration.old';
53+
} from './ReactFiberSyncTaskQueue.old';
5254
import {
5355
NoFlags as NoHookEffect,
5456
Passive as HookPassive,

packages/react-reconciler/src/SchedulerWithReactIntegration.new.js packages/react-reconciler/src/Scheduler.js

-56
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,6 @@ import * as Scheduler from 'scheduler';
1515
import {__interactionsRef} from 'scheduler/tracing';
1616
import {enableSchedulerTracing} from 'shared/ReactFeatureFlags';
1717
import invariant from 'shared/invariant';
18-
import {
19-
DiscreteEventPriority,
20-
getCurrentUpdatePriority,
21-
setCurrentUpdatePriority,
22-
} from './ReactEventPriorities.new';
2318

2419
export const scheduleCallback = Scheduler.unstable_scheduleCallback;
2520
export const cancelCallback = Scheduler.unstable_cancelCallback;
@@ -49,54 +44,3 @@ if (enableSchedulerTracing) {
4944
}
5045

5146
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-
}

0 commit comments

Comments
 (0)