burstFriendlyTokenBucketLimiter
JavaScript
// Token bucket allowing bursts up to capacity while enforcing an average rate.
export class TokenBucket {
constructor(capacity, refillPerSecond, now = () => performance.now()) {
if (!(capacity > 0) || !(refillPerSecond > 0)) throw new RangeError('capacity and rate must be positive');
this.capacity = capacity;
this.tokens = capacity;
this.ratePerMs = refillPerSecond / 1000;
this.lastRefill = now();
this.now = now;
}
#refill() {
const current = this.now();
const elapsed = Math.max(0, current - this.lastRefill);
this.tokens = Math.min(this.capacity, this.tokens + elapsed * this.ratePerMs);
this.lastRefill = current;
}
tryTake(count = 1) {
if (!(count > 0) || count > this.capacity) throw new RangeError('count must be within capacity');
this.#refill();
if (this.tokens < count) return false;
this.tokens -= count;
return true;
}
async acquire(count = 1) {
while (!this.tryTake(count)) {
this.#refill();
const waitMs = Math.ceil((count - this.tokens) / this.ratePerMs);
await new Promise((resolve) => setTimeout(resolve, Math.max(1, waitMs)));
}
}
}