cwait
Version:
Limit number of promises running in parallel
142 lines (135 loc) • 5.24 kB
JavaScript
'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;