bucket-queue
Version:
A queue with leaky bucket logic made for promises
115 lines (97 loc) • 2.93 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = BucketQueue;
var min = Math.min,
max = Math.max;
function BucketQueue() {
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
_ref$calls = _ref.calls,
calls = _ref$calls === undefined ? 100 : _ref$calls,
_ref$perInterval = _ref.perInterval,
perInterval = _ref$perInterval === undefined ? 60 * 1000 : _ref$perInterval,
_ref$maxConcurrent = _ref.maxConcurrent,
maxConcurrent = _ref$maxConcurrent === undefined ? Infinity : _ref$maxConcurrent,
_ref$tickFrequency = _ref.tickFrequency,
tickFrequency = _ref$tickFrequency === undefined ? 10 : _ref$tickFrequency;
var queue = [];
var intervalId = null;
var lastNow = 0;
var bucketCount = 0;
var countdown = 0;
var concurrent = 0;
function _tick() {
var elapsed = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
countdown = max(0, countdown - elapsed);
// reset bucketCount if countdown finishes
if (countdown <= 0) bucketCount = 0;
// get available
var concurrentAvailable = maxConcurrent - concurrent;
var callsAvailable = calls - bucketCount;
var available = max(min(callsAvailable, concurrentAvailable, queue.length), 0);
// if we haven't maxed out
if (bucketCount < calls && available) {
queue.splice(0, available).forEach(function (fn) {
bucketCount += 1;
concurrent += 1;
fn();
});
// reset countdown
countdown = perInterval;
}
}
function _done(cb) {
return function (result) {
concurrent = max(0, concurrent - 1);
cb(result);
};
}
function add(fn) {
var args = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
return new Promise(function (resolve, reject) {
queue.push(function () {
return fn.apply(fn, [].concat(args)).then(_done(resolve)).catch(_done(reject));
});
});
}
function _getElapsed() {
var now = new Date().getTime();
var elapsed = now - lastNow;
lastNow = now;
return elapsed;
}
function start() {
lastNow = new Date().getTime();
_tick(0);
if (!intervalId) intervalId = setInterval(function () {
_tick(_getElapsed());
}, tickFrequency);
return api;
}
function stop() {
if (intervalId) {
clearInterval(intervalId);
intervalId = null;
}
return api;
}
function getState(key) {
var state = {
concurrent: concurrent,
queueCount: queue.length,
bucketCount: bucketCount,
waiting: bucketCount >= calls
};
return key ? state[key] : state;
}
var api = Object.freeze({
getState: getState,
add: add,
start: start,
stop: stop,
_tick: _tick // exporting for tests
});
return api;
}
module.exports = exports["default"];