Async Semaphore Concurrency Limit
Cap concurrent async tasks with a promise-based semaphore that queues waiters and releases permits safely.
Code
export class Semaphore {
constructor(limit) {
if (!Number.isInteger(limit) || limit <= 0) throw new RangeError('limit must be a positive integer');
this.limit = limit;
this.active = 0;
this.waiters = [];
}
async acquire() {
if (this.active < this.limit) {
this.active += 1;
return () => this.release();
}
await new Promise((resolve) => this.waiters.push(resolve));
this.active += 1;
return () => this.release();
}
release() {
if (this.active === 0) throw new Error('release without acquire');
this.active -= 1;
const next = this.waiters.shift();
if (next) {
this.active += 1;
next(() => this.release());
}
}
async run(task) {
const release = await this.acquire();
try {
return await task();
} finally {
release();
}
}
}Report a problem with this exampleOpens a pre-filled issue on GitHub.