UNPKG

bucket-queue

Version:

A queue with leaky bucket logic made for promises

115 lines (97 loc) 2.93 kB
"use strict"; 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"];