@artesoft/timeout-scheduler
Version:
A performance-oriented, freeze-proof scheduler that provides a priority-based task system to prevent UI blocking.
177 lines (176 loc) • 6.5 kB
TypeScript
import { Observable } from 'rxjs';
/**
* Defines the execution strategy for the scheduler.
* - `throughput` (Default): Prioritizes maximum task execution speed by using
* `requestAnimationFrame`. Ideal for UI animations and high-frequency updates.
* - `responsiveness`: Prioritizes main-thread responsiveness by using
* `scheduler.postTask` (if available) to yield frequently.
*/
export type SchedulingStrategy = 'throughput' | 'responsiveness';
/**
* Defines the priority of a task.
* - `user-visible`: High priority, runs sooner in a frame's budget.
* - `background`: Low priority, runs only if time is left in a frame's budget.
*/
export type TaskPriority = 'user-visible' | 'background';
/**
* Options for scheduling a specific task.
*/
export interface TaskOptions {
/** The delay in milliseconds before the task should be executed. */
delay?: number;
/** The priority of the task within the batching queue. */
priority?: TaskPriority;
/**
* Determines if the task should be batched and throttled by the scheduler.
* - `true` (Default): The task is added to the frame loop and executed according
* to the time budget. This is best for UI performance.
* - `false`: The task bypasses the frame loop and uses a dedicated native timer.
* This is required for networking libraries (e.g., Socket.io) that need exact timing
* and cannot tolerate background throttling.
*/
batching?: boolean;
}
/**
* Global configuration for the TimeoutScheduler instance.
*/
export interface SchedulerConfig {
/**
* The primary scheduling strategy to use when the page is visible.
* @default 'throughput'
*/
primaryStrategy?: SchedulingStrategy;
/**
* If true, logs internal state changes to the console.
* @default false
*/
loggingEnabled?: boolean;
/**
* (rAF Mode) If true, automatically adjusts the number of tasks per frame based on execution time.
* @default true
*/
dynamicBudgetEnabled?: boolean;
/**
* (rAF Mode) The target execution time per frame in milliseconds.
* @default 8
*/
frameTimeBudgetMs?: number;
/**
* (rAF Mode) The initial number of tasks to execute per frame.
* @default 50
*/
initialTasksPerFrame?: number;
/**
* (rAF Mode) The maximum number of tasks allowed per frame.
* @default 150
*/
maxTasksPerFrame?: number;
/**
* The interval (in ms) for the background ticker when the tab is hidden.
* Higher values reduce CPU usage; lower values improve background responsiveness.
* @default 250
*/
backgroundTickInterval?: number;
}
/**
* Configuration for the `overrideTimeouts` method.
*/
export interface OverrideOptions {
/**
* A callback hook that allows you to determine the `TaskOptions` dynamically
* whenever `setTimeout` is called.
*
* Use this to inspect the callback, delay, or arguments (or the Error stack)
* to decide if a task should disable batching (e.g., for Socket.io).
*/
getTaskOptions?: (callback: Function, delay: number, args: any[]) => TaskOptions;
}
/**
* A highly configurable, performance-oriented scheduler.
* It intercepts or manages timer tasks to optimize main-thread usage,
* providing frame-budgeting for UI work while allowing critical networking
* tasks to bypass throttling.
*/
export declare class TimeoutScheduler {
private readonly primaryStrategy;
private readonly loggingEnabled;
private readonly dynamicBudgetEnabled;
private readonly frameTimeBudgetMs;
private readonly maxTasksPerFrame;
private readonly initialTasksPerFrame;
private readonly backgroundTickInterval;
private readonly originalSetTimeout;
private readonly originalClearTimeout;
private isOverridden;
private taskIdCounter;
private taskQueue;
private readonly isPostTaskSupported;
private currentSchedulingMode;
private currentTasksPerFrame;
private animationFrameId;
private backgroundTickerId;
/** Emits the current number of pending tasks (both batched and non-batched). */
readonly pendingTaskCount$: Observable<number>;
private readonly pendingTaskCountSubject;
/**
* Constructs an instance of the TimeoutScheduler.
* @param config Configuration options.
*/
constructor(config?: SchedulerConfig);
/**
* Schedules a task.
* @param callback The function to execute.
* @param options Configuration for delay, priority, and batching behavior.
* @returns A unique Task ID (compatible with clearTimeout).
*/
scheduleTask(callback: (...args: any[]) => void, options?: TaskOptions): number;
/**
* Cancels a scheduled task, whether it is batched or non-batched.
* @param taskId The ID returned by scheduleTask.
*/
cancelTask(taskId: number): void;
/**
* Overrides the global `window.setTimeout` and `window.clearTimeout`.
* Allows providing a hook to dynamically configure task options (e.g., disabling batching).
* @param options Configuration for the override behavior.
*/
overrideTimeouts(options?: OverrideOptions): void;
/**
* Restores the original `window.setTimeout` and `window.clearTimeout`.
* Any pending batched tasks are rescheduled to run natively.
*/
restoreTimeouts(): void;
/**
* Destroys the scheduler instance, removing listeners and restoring globals.
*/
destroy(): void;
/**
* Handles visibility changes to switch between rAF (high perf) and setTimeout (background).
*/
private handleVisibilityChange;
/**
* Helper to execute a non-batched task immediately and remove it from the queue.
*/
private executeTaskImmediately;
/**
* Determines the correct scheduling loop based on tasks, strategy, and visibility.
*/
private startAppropriateTicker;
/**
* Stops all active loops (rAF, timeout) and aborts postTask controllers.
*/
private stopAllTickers;
/**
* Schedules a single batched task using scheduler.postTask.
*/
private runTaskWithPostTask;
/**
* The main processing loop used by `rAF` and `timeout` modes.
* Batches tasks and respects the frame budget.
*/
private processRafQueue;
/**
* Adjusts the number of tasks allowed per frame based on how long the previous frame took.
*/
private adjustFrameBudget;
}