/*** Copyright (c) Meta Platforms, Inc. and 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 {PriorityLevel} from '../SchedulerPriorities';
declare class TaskController {
constructor(options?: {priority?: string}): TaskController;
signal: mixed;abort(): void;
}type PostTaskPriorityLevel = 'user-blocking' | 'user-visible' | 'background';
type CallbackNode = {
_controller: TaskController,
};import {
ImmediatePriority,
UserBlockingPriority,
NormalPriority,
LowPriority,
IdlePriority,
} from '../SchedulerPriorities';
export {
ImmediatePriority as unstable_ImmediatePriority,
UserBlockingPriority as unstable_UserBlockingPriority,
NormalPriority as unstable_NormalPriority,
IdlePriority as unstable_IdlePriority,
LowPriority as unstable_LowPriority,
};// Capture local references to native APIs, in case a polyfill overrides them.const perf = window.performance;
const setTimeout = window.setTimeout;
// Use experimental Chrome Scheduler postTask API.const scheduler = global.scheduler;
const getCurrentTime: () => DOMHighResTimeStamp = perf.now.bind(perf);
export const unstable_now = getCurrentTime;
// Scheduler periodically yields in case there is other work on the main// thread, like user events. By default, it yields multiple times per frame.// It does not attempt to align with frame boundaries, since most tasks don't// need to be frame aligned; for those that do, use requestAnimationFrame.const yieldInterval = 5;
let deadline = 0;
let currentPriorityLevel_DEPRECATED = NormalPriority;
// `isInputPending` is not available. Since we have no way of knowing if// there's pending input, always yield at the end of the frame.export function unstable_shouldYield(): boolean {
return getCurrentTime() >= deadline;
}export function unstable_requestPaint() {
// Since we yield every frame regardless, `requestPaint` has no effect.
}type SchedulerCallback<T> = (didTimeout_DEPRECATED: boolean) =>
| T
// May return a continuation
| SchedulerCallback<T>;
export function unstable_scheduleCallback<T>(
priorityLevel: PriorityLevel,
callback: SchedulerCallback<T>,
options?: {delay?: number},
): CallbackNode {
let postTaskPriority;switch (priorityLevel) {
case ImmediatePriority:
case UserBlockingPriority:
postTaskPriority = 'user-blocking';
break;
case LowPriority:
case NormalPriority:
postTaskPriority = 'user-visible';
break;
case IdlePriority:
postTaskPriority = 'background';
break;
default:postTaskPriority = 'user-visible';
break;
}const controller = new TaskController({priority: postTaskPriority});
const postTaskOptions = {delay: typeof options === 'object' && options !== null ? options.delay : 0,
signal: controller.signal,
};const node = {
_controller: controller,
};scheduler
.postTask(
runTask.bind(null, priorityLevel, postTaskPriority, node, callback),
postTaskOptions,
).catch(handleAbortError);
return node;
}function runTask<T>(
priorityLevel: PriorityLevel,
postTaskPriority: PostTaskPriorityLevel,
node: CallbackNode,
callback: SchedulerCallback<T>,
) {
deadline = getCurrentTime() + yieldInterval;
try {currentPriorityLevel_DEPRECATED = priorityLevel;const didTimeout_DEPRECATED = false;const result = callback(didTimeout_DEPRECATED);
if (typeof result === 'function') {
// Assume this is a continuation
const continuation: SchedulerCallback<T> = (result: any);
const continuationOptions = {
signal: node._controller.signal,
};const nextTask = runTask.bind(
null,
priorityLevel,
postTaskPriority,
node,
continuation,
);if (scheduler.yield !== undefined) {
scheduler
.yield(continuationOptions)
.then(nextTask)
.catch(handleAbortError);
} else {
scheduler
.postTask(nextTask, continuationOptions)
.catch(handleAbortError);
}}} catch (error) {
// We're inside a `postTask` promise. If we don't handle this error, then it
// will trigger an "Unhandled promise rejection" error. We don't want that,
// but we do want the default error reporting behavior that normal
// (non-Promise) tasks get for unhandled errors.
//
// So we'll re-throw the error inside a regular browser task.
setTimeout(() => {
throw error;});} finally {
currentPriorityLevel_DEPRECATED = NormalPriority;}}function handleAbortError(error: any) {
// Abort errors are an implementation detail. We don't expose the
// TaskController to the user, nor do we expose the promise that is returned
// from `postTask`. So we should suppress them, since there's no way for the
// user to handle them.
}export function unstable_cancelCallback(node: CallbackNode) {
const controller = node._controller;
controller.abort();
}export function unstable_runWithPriority<T>(
priorityLevel: PriorityLevel,
callback: () => T,
): T {
const previousPriorityLevel = currentPriorityLevel_DEPRECATED;currentPriorityLevel_DEPRECATED = priorityLevel;try {return callback();
} finally {
currentPriorityLevel_DEPRECATED = previousPriorityLevel;}}export function unstable_getCurrentPriorityLevel(): PriorityLevel {
return currentPriorityLevel_DEPRECATED;}export function unstable_next<T>(callback: () => T): T {
let priorityLevel;switch (currentPriorityLevel_DEPRECATED) {
case ImmediatePriority:
case UserBlockingPriority:
case NormalPriority:
// Shift down to normal priority
priorityLevel = NormalPriority;
break;
default:// Anything lower than normal priority should remain at the current level.
priorityLevel = currentPriorityLevel_DEPRECATED;
break;
}const previousPriorityLevel = currentPriorityLevel_DEPRECATED;currentPriorityLevel_DEPRECATED = priorityLevel;try {return callback();
} finally {
currentPriorityLevel_DEPRECATED = previousPriorityLevel;}}export function unstable_wrapCallback<T>(callback: () => T): () => T {
const parentPriorityLevel = currentPriorityLevel_DEPRECATED;return () => {
const previousPriorityLevel = currentPriorityLevel_DEPRECATED;currentPriorityLevel_DEPRECATED = parentPriorityLevel;try {return callback();
} finally {
currentPriorityLevel_DEPRECATED = previousPriorityLevel;}};}export function unstable_forceFrameRate() {}
export function unstable_pauseExecution() {}
export function unstable_continueExecution() {}
export function unstable_getFirstCallbackNode(): null {
return null;}// Currently no profiling buildexport const unstable_Profiling = null;