mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-13 07:04:53 +09:00
Add suffixes like Ms and Ns to variables whos time unit is not in seconds. This is aligning with a new code style of putting units on variables that are not in SI base units.
102 lines
2.6 KiB
TypeScript
102 lines
2.6 KiB
TypeScript
/**
|
|
* Helper class for rate limiting actions
|
|
*
|
|
* Provides a simple interface for implementing actions that are rate
|
|
* limited on invocations.
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* 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.
|
|
* }
|
|
* ```
|
|
*/
|
|
export default class RateLimiter {
|
|
/**
|
|
* Maximum rate of activations per second.
|
|
*/
|
|
maxRate: number;
|
|
/**
|
|
* Callback invoked at most `maxRate` per second on activation.
|
|
*/
|
|
action?: () => void;
|
|
|
|
private _lastRunMs = 0;
|
|
private _runTimeout?: ReturnType<typeof setTimeout>;
|
|
|
|
/**
|
|
* Construct RateLimiter helper
|
|
*
|
|
* The options passed may also be modified on the instance at runtime.
|
|
*
|
|
* @param options - Options for this rate limiter
|
|
* @param options.maxRate -
|
|
* Maximum rate of activations per second.
|
|
* @param options.action -
|
|
* Callback invoked at maste `maxRate` per second on activation.
|
|
*/
|
|
constructor(options: { maxRate?: number, action?: () => void } = {}) {
|
|
this.maxRate = options.maxRate || 1;
|
|
this.action = options.action || undefined;
|
|
}
|
|
|
|
/**
|
|
* 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 true if activation was within the rate limit.
|
|
*/
|
|
activate() {
|
|
let now = Date.now();
|
|
if (now < this._lastRunMs + 1000 / this.maxRate) {
|
|
if (!this._runTimeout && this.action) {
|
|
this._runTimeout = setTimeout(() => {
|
|
this._runTimeout = undefined;
|
|
this._lastRunMs = Date.now();
|
|
if (this.action) {
|
|
this.action();
|
|
}
|
|
}, this._lastRunMs + 1000 / this.maxRate - now);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// We may end up activating before the timeout does.
|
|
if (this._runTimeout) {
|
|
clearTimeout(this._runTimeout);
|
|
this._runTimeout = undefined;
|
|
}
|
|
|
|
this._lastRunMs = now;
|
|
if (this.action) {
|
|
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);
|
|
}
|
|
}
|
|
}
|