UNPKG

ts-async-queue

Version:

A simple async-await task queue written in TypeScript

227 lines (224 loc) 6.64 kB
/** * An error raised during the queue execution */ class QueueError extends Error { constructor(message, queue, data) { super(message) /* istanbul ignore next: because stupid typescript */; this.queue = queue; this.data = data; Object.setPrototypeOf(this, QueueError.prototype); this.name = 'QueueError'; } get failedTask() { return this.queue.currentRunningTask; } get failedTaskIndex() { return this.queue.currentTaskIndex; } toString() { return this.name + ': ' + this.message + '\n' + this.data; } } /** * Manages a queue of async tasks * * @class TaskQueue */ class TaskQueue { constructor( /** * Tasklist */ tasks = []) { this.tasks = tasks; /** * Results of a last queue execution */ this._lastResults = []; /** * `true` if the queue is running * * SHOULD NOT BE MODIFIED outside the class */ this.running = false; /** * An index at which the queue is currenlty running */ this.index = -1; } /** * Results of a last queue execution. * * Empty, if execution hasn't started yet. */ get lastResults() { return this._lastResults.slice(); } /** * `true` if the queue is running */ get isRunning() { return this.running; } /** * A task index at which the queue is currently running * * `-1` if the queue is not currently running */ get currentTaskIndex() { return this.index; } /** * A task which is currently running in the queue * * `undefined` if the queue is not currently running */ get currentRunningTask() { return this.tasks[this.currentTaskIndex]; } /** * Remove a task from queue by its index * * @returns a removed task if found */ dequeueByIndex(index) { if (index > -1 && this.tasks[index]) { const task = this.tasks[index]; this.tasks.splice(index, 1); return task; } return undefined; } /** * Remove a task from queue by its reference. If no task was given, removes the last task. * @param {T} [task] a reference to the task function to remove by * @returns a removed task if found */ dequeueByTask(task) { return this.dequeueByIndex(task ? this.tasks.findIndex(t => t === task) : this.length - 1); } /** * Start executing the queue from a certain point. * Halts if `running` flag is off (pause has occured). * * If any error in any task is raised - pauses queue execution and throws the error upstack. * * @param {number} from * A point to execute a queue from. * @param {Array<any>} lastResults * Saved results to add to. * @param {boolean} running * Internal indication if the method should continue running. * Passing `false` will result in the method not running. * @returns a promise that resolves to task results array when the queue is finished */ launchFrom(from, lastResults = [], running = true) { this._lastResults = lastResults; if (from < this.tasks.length) { this.index = from; if (running) { this.running = running; return this.tasks[from]().then(result => { lastResults.push(result); return this.launchFrom(from + 1, lastResults, this.running); }, e => { this.running = false; throw new QueueError(`Queue paused at task #${from + 1} due to error in handler ${this.tasks[this.index]}`, this, e); }); } } else { this.running = false; } return Promise.resolve(this.lastResults); } /** * Adds one or more tasks to queue. */ enqueue(...tasks) { this.tasks.push.apply(this.tasks, tasks); } dequeue() { const arg = arguments[0]; if (typeof arg === 'number') { return this.dequeueByIndex(arg); } else if (typeof arg === 'function' || !arg) { return this.dequeueByTask(arg); } throw new TypeError('Argument\'s type must either be number, function or undefined!'); } /** * Removes the last task from the queue. * @returns a removed task if found */ pop() { return this.dequeue(); } /** * Get last added task without mutating the queue */ peek() { return this.tasks[this.tasks.length - 1]; } /** * Last added task */ get last() { return this.peek(); } /** * Queue length */ get length() { return this.tasks.length; } /** * Completely clears the queue and stops executions. */ clear() { this.index = -1; this.running = false; this.lastQueue = undefined; this.tasks.splice(0); } /** * Pauses the queue's execution flow after a nearest task is completed. * * @returns a promise that resolves as soon as the queue is paused */ pause() { this.running = false; return this.lastQueue || Promise.resolve([]); } /** * Resumes a previously paused queue. * * @returns a promise that resolves as soon as the queue is completed */ resume() { return this.lastQueue = this.launchFrom(this.index, this._lastResults); } /** * Stops queue execution and clears results. * * @returns a promise that resolves to queue results (or `undefined` if the queue has already been stopeed) as soon as the queue completely stops executing */ stop() { return this.pause() .then(results => (this.clear(), results)); } /** * Starts task queue execution. * * Returns currenlty executed queue if execution already started * * @returns promise with task results as an array sorted by task execution order */ start() { return this.lastQueue = this.lastQueue || this.launchFrom(0); } } export { TaskQueue, QueueError }; //# sourceMappingURL=ts-async-queue.esnext.js.map