UNPKG

async-sequential-runner

Version:

TypeScript class for running async tasks to completion in sequence.

111 lines (110 loc) 4.4 kB
"use strict"; var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var g = generator.apply(thisArg, _arguments || []), i, q = []; return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } function fulfill(value) { resume("next", value); } function reject(value) { resume("throw", value); } function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } }; Object.defineProperty(exports, "__esModule", { value: true }); /** * Helper async generator class that runs async task from a queue, * removing them from the queue when they complete. * @param queue The queue to get operations from. */ function serialAsyncRunner(queue) { return __asyncGenerator(this, arguments, function* serialAsyncRunner_1() { while (queue.length > 0) { const task = queue[0]; yield yield __await(yield __await(task(queue.length > 1).then(v => { queue.shift(); return v; }))); } }); } /** * Runs async tasks sequentially. */ class AsyncSequentialRunner { /** * Runs a task after previous scheduled operations * have run, returning a promise with the result. * @param operation A function that will be called to perform * work, with a boolean indicating if more operations are scheduled. */ async run(task) { const it = this.activeIterator || (() => { const queue = []; const iter = { queue, gen: serialAsyncRunner(queue), }; this.activeIterator = iter; return iter; })(); it.queue.push(task); return it.gen.next().then(itResult => { if (it.queue.length === 0) { this.activeIterator = undefined; } if (this.triggers) { const triggers = this.triggers; this.triggers = undefined; triggers.forEach(resFn => { resFn(); }); } return itResult.value; }); } /** * Creates a trigger with a promise that will complete after the * next task has completed. The trigger can be cancelled by * calling the cancel function. Useful to trigger polling functions * that can schedule a task when the trigger promise completes. */ taskCompleteTrigger() { if (!this.triggers) { this.triggers = []; } const triggers = this.triggers; const p = new Promise((resolve, reject) => { triggers.push(resolve); }); const res = triggers[triggers.length - 1]; return { cancel: () => { if (this.triggers) { const idx = this.triggers.findIndex(v => { return v === res; }); if (idx >= 0) { if (this.triggers.length === 1) { this.triggers = undefined; } else { this.triggers.splice(idx, 1); } } } }, promise: p, }; } /** * Returns whether there are more tasks scheduled or triggers waiting * for task completion, useful for determining when to clean up * references to the runner. */ hasTasksOrTriggers() { return !!((this.triggers && this.triggers.length > 0) || (this.activeIterator && this.activeIterator.queue.length)); } } exports.AsyncSequentialRunner = AsyncSequentialRunner;