/** * @public */ export class RateLimitter { idCounter: number = 0 processingQueue = new Map>() last: number = 0 queue: (() => Promise)[] = [] constructor (readonly config: () => { rate: number, perSecond?: number }) {} async exec = {}>(op: (args?: B) => Promise, args?: B): Promise { const processingId = `${this.idCounter++}` const cfg = this.config() while (this.processingQueue.size > cfg.rate) { await Promise.race(this.processingQueue.values()) } try { const p = op(args) this.processingQueue.set(processingId, p as Promise) return await p } finally { this.processingQueue.delete(processingId) } } async add = {}>(op: (args?: B) => Promise, args?: B): Promise { const cfg = this.config() if (this.processingQueue.size < cfg.rate) { void this.exec(op, args) } else { await this.exec(op, args) } } async waitProcessing (): Promise { await await Promise.race(this.processingQueue.values()) } }