mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-13 15:14:52 +09:00
Move all functions and classes currently available through lib/name and libName to be under a single flat namespace in lib. This make reorganising the lib namespace much simpler as the location of symbols can move without breaking the consumers of lib. The multi-entrypoint design was already problematic for the web plugin system which couldn't effectively use it due to how webpack's shared module system is awkward to set up with multiple entrypoints per package.
105 lines
2.7 KiB
JavaScript
105 lines
2.7 KiB
JavaScript
"use strict";
|
|
|
|
|
|
/**
|
|
* Helper class for rate limiting actions
|
|
*
|
|
* Provides a simple interface for implementing actions that are rate
|
|
* limited on invocations.
|
|
*
|
|
* @example
|
|
* const rateLimiter = RateLimiter({
|
|
* action: () => { console.log("action"); },
|
|
* maxRate: 1,
|
|
* });
|
|
*
|
|
* for (let i = 0; i < 100; i++) {
|
|
* rateLimiter.activate();
|
|
* // Prints action twice to the console, once for the first activation
|
|
* // and one more time 1 second later for 99 other activations that were
|
|
* // exceeding the rate limit.
|
|
* }
|
|
* @alias module:lib.RateLimiter
|
|
*/
|
|
class RateLimiter {
|
|
/**
|
|
* Construct RateLimiter helper
|
|
*
|
|
* The options passed may also be modified on the instance at runtime.
|
|
*
|
|
* @param {object=} options - Options for this rate limiter
|
|
* @param {number=} options.maxRate -
|
|
* Maximum rate of activations per second.
|
|
* @param {function()=} options.action -
|
|
* Callback invoked at maste `maxRate` per second on activation.
|
|
*/
|
|
constructor(options = {}) {
|
|
/**
|
|
* Maximum rate of activations per second.
|
|
* @type {number}
|
|
*/
|
|
this.maxRate = options.maxRate || 1;
|
|
|
|
/**
|
|
* Callback invoked at most `maxRate` per second on activation.
|
|
* @type {function()}
|
|
*/
|
|
this.action = options.action || null;
|
|
|
|
this._lastRun = 0;
|
|
this._runTimeout = null;
|
|
this.lastProgressBroadcast = Date.now();
|
|
}
|
|
|
|
/**
|
|
* Activate the rate limited action
|
|
*
|
|
* Checks if the invocation of the rate limited action now would fall
|
|
* within the rate limit. If it is within the limit then the action is
|
|
* invoked immediatly, otherwise a timeout is registered to invoke the
|
|
* action at next point in time where it will be within the rate limit.
|
|
*
|
|
* @returns {boolean} true if activation was within the rate limit.
|
|
*/
|
|
activate() {
|
|
let now = Date.now();
|
|
if (now < this._lastRun + 1000 / this.maxRate) {
|
|
if (!this._runTimeout && this.action !== null) {
|
|
this._runTimeout = setTimeout(() => {
|
|
this._runTimeout = null;
|
|
this._lastRun = Date.now();
|
|
this.action();
|
|
}, this._lastRun + 1000 / this.maxRate - now);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// We may end up activating before the timeout does.
|
|
if (this._runTimeout) {
|
|
clearTimeout(this._runTimeout);
|
|
this._runTimeout = null;
|
|
}
|
|
|
|
this._lastRun = now;
|
|
if (this.action !== null) {
|
|
this.action();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Cancel pending action
|
|
*
|
|
* Cancel pending action invocation. This is necessary when shutting
|
|
* down the use of the rate limit as a timeout is registered to invoke
|
|
* the next action when activated faster than the rate limit allows.
|
|
*/
|
|
cancel() {
|
|
if (this._runTimeout) {
|
|
clearTimeout(this._runTimeout);
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = RateLimiter;
|