Created
March 24, 2023 21:04
-
-
Save danneu/ce4b6d99e170435fa8cdc61c17233ad1 to your computer and use it in GitHub Desktop.
Revisions
-
danneu created this gist
Mar 24, 2023 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,69 @@ // USAGE const queue = new Queue(50) const middleware = () => async (ctx, next: () => Promise<void>) => { const lock = await queue.take() await next().finally(() => { queue.release(lock) }) } // IMPL class MyLock {} class Ticket { resolve: (_: MyLock) => void constructor(resolve: (_: MyLock) => void) { this.resolve = resolve } } export default class Queue { inflightCapacity: number locks: Set<MyLock> pending: Array<Ticket> pendingCapacity: number constructor(inflightCapacity: number) { this.inflightCapacity = inflightCapacity this.locks = new Set() this.pending = [] this.pendingCapacity = 1000 } // When you take(), you get a promise that resolves into a lock once a lock // is available. async take(): Promise<MyLock> { if (this.locks.size < this.inflightCapacity) { // There are locks available const lock = new MyLock() this.locks.add(lock) return lock } else if (this.pending.length >= this.pendingCapacity) { // Drop request throw new Error('OVER_CAPACITY') } else { // Pend until a lock is available const promise = new Promise((resolve: (_: MyLock) => void) => { const ticket = new Ticket(resolve) this.pending.push(ticket) }) return promise } } release(lock: MyLock) { this.locks.delete(lock) for (let i = 0; i < this.inflightCapacity - this.locks.size; i++) { // const ticket = this.pending.pop() const ticket = this.pending.shift() if (!ticket) { return } const lock = new MyLock() this.locks.add(lock) ticket.resolve(lock) } } }