leaky-bucket-queue
Version:
An implementation of burstable throtling algorithm on top of rxjs
99 lines • 3.69 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var rxjs_1 = require("rxjs");
var ArgumentError_1 = require("./ArgumentError");
/**
* This queue is a simple implementation of leaky bucket algorithm, in terms of number of emitted
* object. The burst object will by default emitted synchronously.
* @public
*/
var LeakyBucketQueue = /** @class */ (function () {
function LeakyBucketQueue(option) {
this.option = option;
this.bucket = 3;
this.subject = new rxjs_1.Subject();
this.queue = [];
this.timerStarted = false;
this.assertOption(option);
this.bucket = option.burstSize;
}
LeakyBucketQueue.prototype.assertOption = function (option) {
if (!option) {
throw ArgumentError_1.ArgumentError.createFromArgumentName('option', 'not be null or undefined');
}
this.assertValue('burstSize', option.burstSize);
this.assertValue('period', option.period);
};
LeakyBucketQueue.prototype.assertValue = function (name, value) {
if (typeof value !== 'number') {
throw ArgumentError_1.ArgumentError.createFromArgumentName(name, 'exist and be a number');
}
if (value < 0) {
throw ArgumentError_1.ArgumentError.createFromArgumentName(name, 'be larger than 0');
}
};
LeakyBucketQueue.prototype.consume = function () {
return this.subject.asObservable();
};
/**
* Queues data for emit at scheduled time. By default burst will be emitted synchronously. If
* consistency is desired (emit item like the delayed item, asynchronously), then a scheduler
* could be used to override this behaviour.
* @param data - data to be queued
*/
LeakyBucketQueue.prototype.enqueue = function (data) {
var _this = this;
this.queue.push(data);
var _loop_1 = function () {
this_1.bucket--;
var data_1 = this_1.queue.shift();
if (this_1.option.scheduler) {
this_1.option.scheduler.schedule(function () { return _this.subject.next(data_1); });
}
else {
this_1.subject.next(data_1);
}
};
var this_1 = this;
while (this.bucket > 0 && this.queue[0]) {
_loop_1();
}
this.startTimer();
};
/** @internal */
LeakyBucketQueue.prototype.startTimer = function () {
var _this = this;
if (this.timerStarted) {
return;
}
this.timer = rxjs_1.timer(this.option.period, this.option.period).subscribe({
next: function () {
var data = _this.queue.shift();
if (data) {
_this.subject.next(data);
}
else {
_this.bucket++;
}
if (_this.bucket === _this.option.burstSize && _this.timer) {
_this.timer.unsubscribe();
_this.timer = undefined;
_this.timerStarted = false;
}
},
});
this.timerStarted = true;
};
/**
* Stops the leaky bucket timer and return remaining item on queue for persistence or clean up.
*/
LeakyBucketQueue.prototype.stop = function () {
if (this.timer) {
this.timer.unsubscribe();
}
return this.queue;
};
return LeakyBucketQueue;
}());
exports.LeakyBucketQueue = LeakyBucketQueue;
//# sourceMappingURL=LeakyBucketQueue.js.map