From 8f8813a19a52e20d242b5017e109da6805d4d4b3 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 24 Sep 2022 15:16:02 -0700 Subject: [PATCH] Add PriorityTaskQueue --- src/common/{Idle.ts => TaskQueue.ts} | 65 +++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 7 deletions(-) rename src/common/{Idle.ts => TaskQueue.ts} (54%) diff --git a/src/common/Idle.ts b/src/common/TaskQueue.ts similarity index 54% rename from src/common/Idle.ts rename to src/common/TaskQueue.ts index e05ad068..49e3f5ec 100644 --- a/src/common/Idle.ts +++ b/src/common/TaskQueue.ts @@ -3,16 +3,24 @@ * @license MIT */ -/** - * A queue of that runs tasks over several idle callbacks, trying to maintain the specified - * frame rate. The tasks will run in the order they are enqueued, but they will run some time later, - * and care should be taken to ensure they're non-urgent and will not introduce race conditions. - */ -export class IdleTaskQueue { +import { isNode } from 'common/Platform'; + +interface ITaskQueue { +} + +interface ITaskDeadline { + timeRemaining(): number; +} +type CallbackWithDeadline = (deadline: ITaskDeadline) => void; + +abstract class TaskQueue implements ITaskQueue { private _tasks: (() => void)[] = []; private _idleCallback?: number; private _i = 0; + protected abstract _requestCallback(callback: CallbackWithDeadline): number; + protected abstract _cancelCallback(identifier: number): void; + /** * Adds a task to the queue which will run in a future idle callback. */ @@ -69,12 +77,55 @@ export class IdleTaskQueue { } } +/** + * A queue of that runs tasks over several tasks via setTimeout, trying to maintain above 60 frames + * per second. The tasks will run in the order they are enqueued, but they will run some time later, + * and care should be taken to ensure they're non-urgent and will not introduce race conditions. + */ +export class PriorityTaskQueue extends TaskQueue { + protected _requestCallback(callback: CallbackWithDeadline): number { + return setTimeout(() => callback(this._createDeadline(16))); + } + + protected _cancelCallback(identifier: number): void { + clearTimeout(identifier); + } + + private _createDeadline(duration: number): ITaskDeadline { + const end = performance.now() + duration; + return { + timeRemaining: () => Math.max(0, end - performance.now()) + }; + } +} + +class IdleTaskQueueInternal extends TaskQueue { + protected _requestCallback(callback: IdleRequestCallback): number { + return requestIdleCallback(callback); + } + + protected _cancelCallback(identifier: number): void { + cancelIdleCallback(identifier); + } +} + +/** + * A queue of that runs tasks over several idle callbacks, trying to respect the idle callback's + * deadline given by the environment. The tasks will run in the order they are enqueued, but they + * will run some time later, and care should be taken to ensure they're non-urgent and will not + * introduce race conditions. + * + * This reverts to a {@link PriorityTaskQueue} if the environment does not support idle callbacks. + */ +// eslint-disable-next-line @typescript-eslint/naming-convention +export const IdleTaskQueue = (!isNode && 'requestIdleCallback' in window) ? IdleTaskQueueInternal : PriorityTaskQueue; + /** * An object that tracks a single debounced task that will run on the next idle frame. When called * multiple times, only the last set task will run. */ export class DebouncedIdleTask { - private _queue: IdleTaskQueue; + private _queue: IdleTaskQueueInternal | PriorityTaskQueue; constructor() { this._queue = new IdleTaskQueue();