cwait
Version:
Limit number of promises running in parallel
92 lines (91 loc) • 3.66 kB
JavaScript
// This file is part of cwait, copyright (c) 2015- BusFaster Ltd.
// Released under the MIT license, see LICENSE.
import { BinaryHeap } from 'cdata';
import { Task, tryFinally } from './Task';
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 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;
}());
export { TaskQueue };