mirror of
https://github.com/facebook/react.git
synced 2026-02-26 18:58:05 +00:00
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.
This commit is contained in:
@@ -34,25 +34,21 @@ import {
|
||||
import getEventTarget from './getEventTarget';
|
||||
import {getClosestInstanceFromNode} from '../client/ReactDOMComponentTree';
|
||||
|
||||
import {
|
||||
enableLegacyFBSupport,
|
||||
enableNewReconciler,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import {enableLegacyFBSupport} from 'shared/ReactFeatureFlags';
|
||||
import {dispatchEventForPluginEventSystem} from './DOMPluginEventSystem';
|
||||
import {
|
||||
flushDiscreteUpdatesIfNeeded,
|
||||
discreteUpdates,
|
||||
} from './ReactDOMUpdateBatching';
|
||||
|
||||
import {getCurrentPriorityLevel as getCurrentPriorityLevel_old} from 'react-reconciler/src/SchedulerWithReactIntegration.old';
|
||||
import {
|
||||
getCurrentPriorityLevel as getCurrentPriorityLevel_new,
|
||||
getCurrentPriorityLevel as getCurrentSchedulerPriorityLevel,
|
||||
IdlePriority as IdleSchedulerPriority,
|
||||
ImmediatePriority as ImmediateSchedulerPriority,
|
||||
LowPriority as LowSchedulerPriority,
|
||||
NormalPriority as NormalSchedulerPriority,
|
||||
UserBlockingPriority as UserBlockingSchedulerPriority,
|
||||
} from 'react-reconciler/src/SchedulerWithReactIntegration.new';
|
||||
} from 'react-reconciler/src/Scheduler';
|
||||
import {
|
||||
DiscreteEventPriority,
|
||||
ContinuousEventPriority,
|
||||
@@ -62,10 +58,6 @@ import {
|
||||
setCurrentUpdatePriority,
|
||||
} from 'react-reconciler/src/ReactEventPriorities';
|
||||
|
||||
const getCurrentPriorityLevel = enableNewReconciler
|
||||
? getCurrentPriorityLevel_new
|
||||
: getCurrentPriorityLevel_old;
|
||||
|
||||
// TODO: can we stop exporting these?
|
||||
export let _enabled = true;
|
||||
|
||||
@@ -392,7 +384,7 @@ export function getEventPriority(domEventName: DOMEventName): * {
|
||||
// We might be in the Scheduler callback.
|
||||
// Eventually this mechanism will be replaced by a check
|
||||
// of the current priority on the native scheduler.
|
||||
const schedulerPriority = getCurrentPriorityLevel();
|
||||
const schedulerPriority = getCurrentSchedulerPriorityLevel();
|
||||
switch (schedulerPriority) {
|
||||
case ImmediateSchedulerPriority:
|
||||
return DiscreteEventPriority;
|
||||
|
||||
@@ -28,7 +28,7 @@ import type {Cache, SpawnedCachePool} from './ReactFiberCacheComponent.new';
|
||||
|
||||
import {resetWorkInProgressVersions as resetMutableSourceWorkInProgressVersions} from './ReactMutableSource.new';
|
||||
|
||||
import {now} from './SchedulerWithReactIntegration.new';
|
||||
import {now} from './Scheduler';
|
||||
|
||||
import {
|
||||
IndeterminateComponent,
|
||||
|
||||
@@ -28,7 +28,7 @@ import type {Cache, SpawnedCachePool} from './ReactFiberCacheComponent.old';
|
||||
|
||||
import {resetWorkInProgressVersions as resetMutableSourceWorkInProgressVersions} from './ReactMutableSource.old';
|
||||
|
||||
import {now} from './SchedulerWithReactIntegration.old';
|
||||
import {now} from './Scheduler';
|
||||
|
||||
import {
|
||||
IndeterminateComponent,
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
UserBlockingPriority as UserBlockingSchedulerPriority,
|
||||
NormalPriority as NormalSchedulerPriority,
|
||||
IdlePriority as IdleSchedulerPriority,
|
||||
} from './SchedulerWithReactIntegration.new';
|
||||
} from './Scheduler';
|
||||
|
||||
declare var __REACT_DEVTOOLS_GLOBAL_HOOK__: Object | void;
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
UserBlockingPriority as UserBlockingSchedulerPriority,
|
||||
NormalPriority as NormalSchedulerPriority,
|
||||
IdlePriority as IdleSchedulerPriority,
|
||||
} from './SchedulerWithReactIntegration.old';
|
||||
} from './Scheduler';
|
||||
|
||||
declare var __REACT_DEVTOOLS_GLOBAL_HOOK__: Object | void;
|
||||
|
||||
|
||||
67
packages/react-reconciler/src/ReactFiberSyncTaskQueue.new.js
Normal file
67
packages/react-reconciler/src/ReactFiberSyncTaskQueue.new.js
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import type {SchedulerCallback} from './Scheduler';
|
||||
|
||||
import {
|
||||
DiscreteEventPriority,
|
||||
getCurrentUpdatePriority,
|
||||
setCurrentUpdatePriority,
|
||||
} from './ReactEventPriorities.new';
|
||||
import {ImmediatePriority, scheduleCallback} from './Scheduler';
|
||||
|
||||
let syncQueue: Array<SchedulerCallback> | null = null;
|
||||
let isFlushingSyncQueue: boolean = false;
|
||||
|
||||
export function scheduleSyncCallback(callback: SchedulerCallback) {
|
||||
// Push this callback into an internal queue. We'll flush these either in
|
||||
// the next tick, or earlier if something calls `flushSyncCallbackQueue`.
|
||||
if (syncQueue === null) {
|
||||
syncQueue = [callback];
|
||||
} else {
|
||||
// Push onto existing queue. Don't need to schedule a callback because
|
||||
// we already scheduled one when we created the queue.
|
||||
syncQueue.push(callback);
|
||||
}
|
||||
}
|
||||
|
||||
export function flushSyncCallbackQueue() {
|
||||
if (!isFlushingSyncQueue && syncQueue !== null) {
|
||||
// Prevent re-entrancy.
|
||||
isFlushingSyncQueue = true;
|
||||
let i = 0;
|
||||
const previousUpdatePriority = getCurrentUpdatePriority();
|
||||
try {
|
||||
const isSync = true;
|
||||
const queue = syncQueue;
|
||||
// TODO: Is this necessary anymore? The only user code that runs in this
|
||||
// queue is in the render or commit phases.
|
||||
setCurrentUpdatePriority(DiscreteEventPriority);
|
||||
for (; i < queue.length; i++) {
|
||||
let callback = queue[i];
|
||||
do {
|
||||
callback = callback(isSync);
|
||||
} while (callback !== null);
|
||||
}
|
||||
syncQueue = null;
|
||||
} catch (error) {
|
||||
// If something throws, leave the remaining callbacks on the queue.
|
||||
if (syncQueue !== null) {
|
||||
syncQueue = syncQueue.slice(i + 1);
|
||||
}
|
||||
// Resume flushing in the next tick
|
||||
scheduleCallback(ImmediatePriority, flushSyncCallbackQueue);
|
||||
throw error;
|
||||
} finally {
|
||||
setCurrentUpdatePriority(previousUpdatePriority);
|
||||
isFlushingSyncQueue = false;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -7,50 +7,15 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
// This module only exists as an ESM wrapper around the external CommonJS
|
||||
// Scheduler dependency. Notice that we're intentionally not using named imports
|
||||
// because Rollup would use dynamic dispatch for CommonJS interop named imports.
|
||||
// When we switch to ESM, we can delete this module.
|
||||
import * as Scheduler from 'scheduler';
|
||||
import {__interactionsRef} from 'scheduler/tracing';
|
||||
import {enableSchedulerTracing} from 'shared/ReactFeatureFlags';
|
||||
import invariant from 'shared/invariant';
|
||||
import type {SchedulerCallback} from './Scheduler';
|
||||
|
||||
import {
|
||||
DiscreteEventPriority,
|
||||
getCurrentUpdatePriority,
|
||||
setCurrentUpdatePriority,
|
||||
} from './ReactEventPriorities.old';
|
||||
import {ImmediatePriority, scheduleCallback} from './Scheduler';
|
||||
|
||||
export const scheduleCallback = Scheduler.unstable_scheduleCallback;
|
||||
export const cancelCallback = Scheduler.unstable_cancelCallback;
|
||||
export const shouldYield = Scheduler.unstable_shouldYield;
|
||||
export const requestPaint = Scheduler.unstable_requestPaint;
|
||||
export const now = Scheduler.unstable_now;
|
||||
export const getCurrentPriorityLevel =
|
||||
Scheduler.unstable_getCurrentPriorityLevel;
|
||||
export const ImmediatePriority = Scheduler.unstable_ImmediatePriority;
|
||||
export const UserBlockingPriority = Scheduler.unstable_UserBlockingPriority;
|
||||
export const NormalPriority = Scheduler.unstable_NormalPriority;
|
||||
export const LowPriority = Scheduler.unstable_LowPriority;
|
||||
export const IdlePriority = Scheduler.unstable_IdlePriority;
|
||||
|
||||
if (enableSchedulerTracing) {
|
||||
// Provide explicit error message when production+profiling bundle of e.g.
|
||||
// react-dom is used with production (non-profiling) bundle of
|
||||
// scheduler/tracing
|
||||
invariant(
|
||||
__interactionsRef != null && __interactionsRef.current != null,
|
||||
'It is not supported to run the profiling version of a renderer (for ' +
|
||||
'example, `react-dom/profiling`) without also replacing the ' +
|
||||
'`scheduler/tracing` module with `scheduler/tracing-profiling`. Your ' +
|
||||
'bundler might have a setting for aliasing both modules. Learn more at ' +
|
||||
'https://reactjs.org/link/profiling',
|
||||
);
|
||||
}
|
||||
|
||||
export type SchedulerCallback = (isSync: boolean) => SchedulerCallback | null;
|
||||
|
||||
// TODO: Move sync task queue to its own module.
|
||||
let syncQueue: Array<SchedulerCallback> | null = null;
|
||||
let isFlushingSyncQueue: boolean = false;
|
||||
|
||||
@@ -46,9 +46,11 @@ import {
|
||||
UserBlockingPriority as UserBlockingSchedulerPriority,
|
||||
NormalPriority as NormalSchedulerPriority,
|
||||
IdlePriority as IdleSchedulerPriority,
|
||||
} from './Scheduler';
|
||||
import {
|
||||
flushSyncCallbackQueue,
|
||||
scheduleSyncCallback,
|
||||
} from './SchedulerWithReactIntegration.new';
|
||||
} from './ReactFiberSyncTaskQueue.new';
|
||||
import {
|
||||
NoFlags as NoHookEffect,
|
||||
Passive as HookPassive,
|
||||
|
||||
@@ -46,9 +46,11 @@ import {
|
||||
UserBlockingPriority as UserBlockingSchedulerPriority,
|
||||
NormalPriority as NormalSchedulerPriority,
|
||||
IdlePriority as IdleSchedulerPriority,
|
||||
} from './Scheduler';
|
||||
import {
|
||||
flushSyncCallbackQueue,
|
||||
scheduleSyncCallback,
|
||||
} from './SchedulerWithReactIntegration.old';
|
||||
} from './ReactFiberSyncTaskQueue.old';
|
||||
import {
|
||||
NoFlags as NoHookEffect,
|
||||
Passive as HookPassive,
|
||||
|
||||
@@ -15,11 +15,6 @@ import * as Scheduler from 'scheduler';
|
||||
import {__interactionsRef} from 'scheduler/tracing';
|
||||
import {enableSchedulerTracing} from 'shared/ReactFeatureFlags';
|
||||
import invariant from 'shared/invariant';
|
||||
import {
|
||||
DiscreteEventPriority,
|
||||
getCurrentUpdatePriority,
|
||||
setCurrentUpdatePriority,
|
||||
} from './ReactEventPriorities.new';
|
||||
|
||||
export const scheduleCallback = Scheduler.unstable_scheduleCallback;
|
||||
export const cancelCallback = Scheduler.unstable_cancelCallback;
|
||||
@@ -49,54 +44,3 @@ if (enableSchedulerTracing) {
|
||||
}
|
||||
|
||||
export type SchedulerCallback = (isSync: boolean) => SchedulerCallback | null;
|
||||
|
||||
// TODO: Move sync task queue to its own module.
|
||||
let syncQueue: Array<SchedulerCallback> | null = null;
|
||||
let isFlushingSyncQueue: boolean = false;
|
||||
|
||||
export function scheduleSyncCallback(callback: SchedulerCallback) {
|
||||
// Push this callback into an internal queue. We'll flush these either in
|
||||
// the next tick, or earlier if something calls `flushSyncCallbackQueue`.
|
||||
if (syncQueue === null) {
|
||||
syncQueue = [callback];
|
||||
} else {
|
||||
// Push onto existing queue. Don't need to schedule a callback because
|
||||
// we already scheduled one when we created the queue.
|
||||
syncQueue.push(callback);
|
||||
}
|
||||
}
|
||||
|
||||
export function flushSyncCallbackQueue() {
|
||||
if (!isFlushingSyncQueue && syncQueue !== null) {
|
||||
// Prevent re-entrancy.
|
||||
isFlushingSyncQueue = true;
|
||||
let i = 0;
|
||||
const previousUpdatePriority = getCurrentUpdatePriority();
|
||||
try {
|
||||
const isSync = true;
|
||||
const queue = syncQueue;
|
||||
// TODO: Is this necessary anymore? The only user code that runs in this
|
||||
// queue is in the render or commit phases.
|
||||
setCurrentUpdatePriority(DiscreteEventPriority);
|
||||
for (; i < queue.length; i++) {
|
||||
let callback = queue[i];
|
||||
do {
|
||||
callback = callback(isSync);
|
||||
} while (callback !== null);
|
||||
}
|
||||
syncQueue = null;
|
||||
} catch (error) {
|
||||
// If something throws, leave the remaining callbacks on the queue.
|
||||
if (syncQueue !== null) {
|
||||
syncQueue = syncQueue.slice(i + 1);
|
||||
}
|
||||
// Resume flushing in the next tick
|
||||
scheduleCallback(ImmediatePriority, flushSyncCallbackQueue);
|
||||
throw error;
|
||||
} finally {
|
||||
setCurrentUpdatePriority(previousUpdatePriority);
|
||||
isFlushingSyncQueue = false;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user