Source: rendering/renderers/shared/SchedulerSystem.ts

import { ExtensionType } from '../../../extensions/Extensions';
import { Ticker } from '../../../ticker/Ticker';

import type { System } from './system/System';

// start at one too keep it positive!
let uid = 1;

/**
 * The SchedulerSystem manages scheduled tasks with specific intervals.
 * @memberof rendering
 */
export class SchedulerSystem implements System<null>
{
    /** @ignore */
    public static extension = {
        type: [
            ExtensionType.WebGLSystem,
            ExtensionType.WebGPUSystem,
            ExtensionType.CanvasSystem,
        ],
        name: 'scheduler',
        priority: 0,
    } as const;

    private readonly _tasks: {
        func: (elapsed: number) => void;
        duration: number;
        offset: number
        start: number;
        last: number;
        repeat: boolean;
        id: number;
    }[] = [];

    /** a small off set to apply to the repeat schedules. This is just to make sure they run at slightly different times */
    private _offset = 0;

    /** Initializes the scheduler system and starts the ticker. */
    public init(): void
    {
        Ticker.system.add(this._update, this);
    }

    /**
     * Schedules a repeating task.
     * @param func - The function to execute.
     * @param duration - The interval duration in milliseconds.
     * @param useOffset - this will spread out tasks so that they do not all run at the same time
     * @returns The unique identifier for the scheduled task.
     */
    public repeat(func: (elapsed: number) => void, duration: number, useOffset = true): number
    {
        const id = uid++;

        let offset = 0;

        if (useOffset)
        {
            this._offset += 1000;
            offset = this._offset;
        }

        this._tasks.push({
            func,
            duration,
            start: performance.now(),
            offset,
            last: performance.now(),
            repeat: true,
            id
        });

        return id;
    }

    /**
     * Cancels a scheduled task.
     * @param id - The unique identifier of the task to cancel.
     */
    public cancel(id: number): void
    {
        for (let i = 0; i < this._tasks.length; i++)
        {
            if (this._tasks[i].id === id)
            {
                this._tasks.splice(i, 1);

                return;
            }
        }
    }

    /**
     * Updates and executes the scheduled tasks.
     * @private
     */
    private _update(): void
    {
        const now = performance.now();

        for (let i = 0; i < this._tasks.length; i++)
        {
            const task = this._tasks[i];

            if ((now - task.offset) - task.last >= task.duration)
            {
                const elapsed = now - task.start;

                task.func(elapsed);
                task.last = now;
            }
        }
    }

    /**
     * Destroys the scheduler system and removes all tasks.
     * @internal
     * @ignore
     */
    public destroy(): void
    {
        Ticker.system.remove(this._update, this);

        this._tasks.length = 0;
    }
}