88 lines
2.9 KiB
JavaScript
88 lines
2.9 KiB
JavaScript
/**
|
|
* 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 includesLegacySyncCallbacks: boolean = false;
|
|
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 scheduleLegacySyncCallback(callback: SchedulerCallback) {
|
|
includesLegacySyncCallbacks = true;
|
|
scheduleSyncCallback(callback);
|
|
}
|
|
|
|
export function flushSyncCallbacksOnlyInLegacyMode() {
|
|
// Only flushes the queue if there's a legacy sync callback scheduled.
|
|
// TODO: There's only a single type of callback: performSyncOnWorkOnRoot. So
|
|
// it might make more sense for the queue to be a list of roots instead of a
|
|
// list of generic callbacks. Then we can have two: one for legacy roots, one
|
|
// for concurrent roots. And this method would only flush the legacy ones.
|
|
if (includesLegacySyncCallbacks) {
|
|
flushSyncCallbacks();
|
|
}
|
|
}
|
|
|
|
export function flushSyncCallbacks(): null {
|
|
if (!isFlushingSyncQueue && syncQueue !== null) {
|
|
// Prevent re-entrance.
|
|
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);
|
|
// $FlowFixMe[incompatible-use] found when upgrading Flow
|
|
for (; i < queue.length; i++) {
|
|
// $FlowFixMe[incompatible-use] found when upgrading Flow
|
|
let callback = queue[i];
|
|
do {
|
|
callback = callback(isSync);
|
|
} while (callback !== null);
|
|
}
|
|
syncQueue = null;
|
|
includesLegacySyncCallbacks = false;
|
|
} 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, flushSyncCallbacks);
|
|
throw error;
|
|
} finally {
|
|
setCurrentUpdatePriority(previousUpdatePriority);
|
|
isFlushingSyncQueue = false;
|
|
}
|
|
}
|
|
return null;
|
|
}
|