Token Bucket Throttle With Burst Capacity
A token bucket limiter that refills at a steady rate, allows bursts up to capacity, and reports allow and deny decisions with a deterministic clock.
Code
export class TokenBucket {
constructor(options) {
const { capacity, refillPerSecond, now = () => Date.now() } = options;
if (!Number.isInteger(capacity) || capacity < 1) {
throw new RangeError("capacity must be a positive integer");
}
if (!Number.isFinite(refillPerSecond) || refillPerSecond <= 0) {
throw new RangeError("refillPerSecond must be a positive number");
}
this.capacity = capacity;
this.refillPerSecond = refillPerSecond;
this.now = now;
this.tokens = capacity;
this.lastRefill = now();
}
get available() {
this.refill();
return this.tokens;
}
tryConsume(cost = 1) {
if (!Number.isFinite(cost) || cost <= 0) {
throw new RangeError("cost must be a positive number");
}
if (cost > this.capacity) {
throw new RangeError(`cost ${cost} exceeds bucket capacity ${this.capacity}`);
}
this.refill();
if (this.tokens < cost) {
return { allowed: false, remaining: this.tokens };
}
this.tokens -= cost;
return { allowed: true, remaining: this.tokens };
}
refill() {
const t = this.now();
const elapsedMs = Math.max(0, t - this.lastRefill);
const gained = (elapsedMs / 1000) * this.refillPerSecond;
this.tokens = Math.min(this.capacity, this.tokens + gained);
this.lastRefill = t;
}
}Report a problem with this exampleOpens a pre-filled issue on GitHub.