UNPKG

cwait

Version:

Limit number of promises running in parallel

142 lines (135 loc) 5.24 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var cdata = require('cdata'); // This file is part of cwait, copyright (c) 2015- BusFaster Ltd. // Released under the MIT license, see LICENSE. /** Call func and return a promise for its result. * Optionally call given resolve or reject handler when the promise settles. */ function tryFinally(func, onFinish, Promise, resolve, reject) { var promise; try { promise = Promise.resolve(func()); } catch (err) { // If func threw an error, return a rejected promise. promise = Promise.reject(err); } promise.then(onFinish, onFinish); if (resolve) promise.then(resolve, reject); return (promise); } /** Task wraps a promise, delaying it until some resource gets less busy. */ var Task = /** @class */ (function () { function Task(func, Promise, stamp) { this.func = func; this.Promise = Promise; this.stamp = stamp; } /** Wrap task result in a new promise so it can be resolved later. */ Task.prototype.delay = function () { var _this = this; if (!this.promise) { this.promise = new this.Promise(function (resolve, reject) { _this.resolve = resolve; _this.reject = reject; }); } return (this.promise); }; /** Start the task and call onFinish when done. */ Task.prototype.resume = function (onFinish) { return (tryFinally(this.func, onFinish, this.Promise, this.resolve, this.reject)); }; return Task; }()); // This file is part of cwait, copyright (c) 2015- BusFaster Ltd. var TaskQueue = /** @class */ (function () { function TaskQueue(Promise, /** Number of promises allowed to resolve concurrently. */ concurrency) { var _this = this; this.Promise = Promise; this.concurrency = concurrency; this.busyCount = 0; this.backlog = new cdata.BinaryHeap(function (a, b) { return a.stamp - b.stamp; }); this.Promise = Promise; this.concurrency = concurrency; this.nextBound = function () { return _this.next(1); }; this.nextTickBound = function () { _this.timerStamp = 0; _this.next(0); }; } /** Add a new task to the queue. * It will start when the number of other concurrent tasks is low enough. * @param func Function to call. * @param delay Initial delay in milliseconds before making the call. */ TaskQueue.prototype.add = function (func, delay) { if (delay === void 0) { delay = 0; } if (this.busyCount < this.concurrency && !delay) { // Start the task immediately. ++this.busyCount; return (tryFinally(func, this.nextBound, this.Promise)); } else { // Schedule the task and return a promise resolving // to the result of task.start(). var stamp = new Date().getTime() + delay; var task = new Task(func, this.Promise, stamp); this.backlog.insert(task); return (task.delay()); } }; /** Consider current function idle until promise resolves. * Useful for making recursive calls. */ TaskQueue.prototype.unblock = function (promise) { var _this = this; this.next(1); var onFinish = function () { return ++_this.busyCount; }; promise.then(onFinish, onFinish); return (promise); }; TaskQueue.prototype.wrap = function (func, thisObject) { var _this = this; return (function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } return _this.add(function () { return func.apply(thisObject, args); }); }); }; /** Start the next task from the backlog. */ TaskQueue.prototype.next = function (ended) { var stamp = new Date().getTime(); var task = null; this.busyCount -= ended; // If another task is eligible to run, get the next scheduled task. if (this.busyCount < this.concurrency) task = this.backlog.peekTop(); if (!task) return; if (task.stamp <= stamp) { // A task remains, scheduled to start already. Resume it. ++this.busyCount; task = this.backlog.extractTop(); task.resume(this.nextBound); } else if (!this.timerStamp || task.stamp + 1 < this.timerStamp) { // There is a task scheduled after a delay, // and no current timer firing before the delay. if (this.timerStamp) { // There is a timer firing too late. Remove it. clearTimeout(this.timer); } // Start a timer to clear timerStamp and call this function. this.timer = setTimeout(this.nextTickBound, ~~(task.stamp - stamp + 1)); this.timerStamp = task.stamp; } }; return TaskQueue; }()); // This file is part of cwait, copyright (c) 2015- BusFaster Ltd. exports.Task = Task; exports.TaskQueue = TaskQueue;