bucket-queue
Version:
A queue with leaky bucket logic made for promises
289 lines (227 loc) • 7.77 kB
JavaScript
'use strict';
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
var test = require('ava');
var defer = require('promise-defer');
var BucketQueue = require('./');
var makeCompareState = function makeCompareState(q, equal) {
return function (want) {
var message = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'same state';
var got = q.getState();
var desired = Object.assign({}, got, want);
equal(got, desired, message);
};
};
test('should maintain proper state', function () {
var _ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee(t) {
var q, compareState, d, value, p, result;
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
q = BucketQueue({
calls: 1,
perInterval: 150
});
compareState = makeCompareState(q, function (got, want, message) {
t.deepEqual(got, want, message);
});
compareState({
waiting: false,
bucketCount: 0,
queueCount: 0,
concurrent: 0
}, 'initialized queue');
d = defer();
value = 'cat';
p = q.add(function () {
return d.promise;
});
compareState({
waiting: false,
bucketCount: 0,
queueCount: 1,
concurrent: 0
}, 'added promise returning function to the queue');
// start
q._tick(0);
compareState({
waiting: true,
bucketCount: 1,
queueCount: 0,
concurrent: 1
}, 'function popped off the queue and called');
d.resolve(value);
_context.next = 12;
return p;
case 12:
result = _context.sent;
t.is(result, value, 'promise resolve value should be: ' + value);
compareState({
waiting: true,
bucketCount: 1,
queueCount: 0,
concurrent: 0
}, 'promise resolved');
q._tick(50);
compareState({
waiting: true,
bucketCount: 1,
queueCount: 0,
concurrent: 0
}, 'countdown is at 150ms');
q._tick(150);
compareState({
waiting: false,
bucketCount: 0,
queueCount: 0,
concurrent: 0
}, 'countdown is at 0ms and bucketCount is reset');
case 19:
case 'end':
return _context.stop();
}
}
}, _callee, undefined);
}));
return function (_x2) {
return _ref.apply(this, arguments);
};
}());
test('should throttle calls', function () {
var _ref2 = _asyncToGenerator(regeneratorRuntime.mark(function _callee2(t) {
var q, compareState, d1, d2, d3, p1, p2, p3, value1, result1, value2, result2, value3, result3;
return regeneratorRuntime.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
q = BucketQueue({
calls: 2,
perInterval: 200
});
compareState = makeCompareState(q, function (got, want, message) {
t.deepEqual(got, want, message);
});
compareState({
waiting: false,
bucketCount: 0,
queueCount: 0,
concurrent: 0
}, 'initialized queue');
d1 = defer();
d2 = defer();
d3 = defer();
p1 = q.add(function () {
return d1.promise;
});
p2 = q.add(function () {
return d2.promise;
});
p3 = q.add(function () {
return d3.promise;
});
compareState({
waiting: false,
bucketCount: 0,
queueCount: 3,
concurrent: 0
}, 'added 3 promise returning fuctions to queue');
// start
q._tick(0);
compareState({
waiting: true,
bucketCount: 2,
queueCount: 1,
concurrent: 2
}, 'max functions poped off the queue and called');
q._tick(50);
compareState({
waiting: true,
bucketCount: 2,
queueCount: 1,
concurrent: 2
}, 'countdown is at 150ms');
value1 = 'cat';
d1.resolve(value1);
_context2.next = 18;
return p1;
case 18:
result1 = _context2.sent;
t.is(result1, value1, 'promise1 resolve value should be: ' + value1);
compareState({
waiting: true,
bucketCount: 2,
queueCount: 1,
concurrent: 1
}, 'promise 1 resolves');
q._tick(50);
compareState({
waiting: true,
bucketCount: 2,
queueCount: 1,
concurrent: 1
}, 'countdown is at 100ms');
value2 = 'mouse';
d2.resolve(value2);
_context2.next = 27;
return p2;
case 27:
result2 = _context2.sent;
t.is(result2, value2, 'promise2 resolve value should be: ' + value2);
compareState({
waiting: true,
bucketCount: 2,
queueCount: 1,
concurrent: 0
}, 'promise 2 resolves');
q._tick(50);
compareState({
waiting: true,
bucketCount: 2,
queueCount: 1,
concurrent: 0
}, 'countdown is at 50ms');
q._tick(50);
compareState({
waiting: false,
bucketCount: 1,
queueCount: 0,
concurrent: 1
}, 'countdown finished and function3 popped off the queue and called');
value3 = 'cheese';
d3.resolve(value3);
_context2.next = 38;
return p3;
case 38:
result3 = _context2.sent;
t.is(result3, value3, 'promise3 resolve value should be: ' + value3);
compareState({
waiting: false,
bucketCount: 1,
queueCount: 0,
concurrent: 0
}, 'promise 3 resolves');
q._tick(50);
compareState({
waiting: false,
bucketCount: 1,
queueCount: 0,
concurrent: 0
}, 'countdown is at 150ms');
q._tick(150);
compareState({
waiting: false,
bucketCount: 0,
queueCount: 0,
concurrent: 0
}, 'countdown is at 0ms and bucketCount is reset');
case 45:
case 'end':
return _context2.stop();
}
}
}, _callee2, undefined);
}));
return function (_x3) {
return _ref2.apply(this, arguments);
};
}());