// @flow strict import assert from 'assert'; export type TaskID = number; // can't mark opaque due to https://github.com/flowtype/flow-remove-types/pull/61 type Task = { callback: (timeStamp: number) => void; id: TaskID; cancelled: boolean; }; class TaskQueue { _queue: Array; _id: TaskID; _cleared: boolean; _currentlyRunning: Array | false; constructor() { this._queue = []; this._id = 0; this._cleared = false; this._currentlyRunning = false; } add(callback: (timeStamp: number) => void): TaskID { const id = ++this._id; const queue = this._queue; queue.push({callback, id, cancelled: false}); return id; } remove(id: TaskID) { const running = this._currentlyRunning; const queue = running ? this._queue.concat(running) : this._queue; for (const task of queue) { if (task.id === id) { task.cancelled = true; return; } } } run(timeStamp: number = 0) { assert(!this._currentlyRunning); const queue = this._currentlyRunning = this._queue; // Tasks queued by callbacks in the current queue should be executed // on the next run, not the current run. this._queue = []; for (const task of queue) { if (task.cancelled) continue; task.callback(timeStamp); if (this._cleared) break; } this._cleared = false; this._currentlyRunning = false; } clear() { if (this._currentlyRunning) { this._cleared = true; } this._queue = []; } } export default TaskQueue;