There isn't anything built-in for this, so you'll have to build your own. AFAIK, there's no library for this yet, either.
First, start with a "deferral" - a promise that allows external code to resolve it:
class Deferral<T> {
constructor() {
this.promise = new Promise<T>((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
}
promise: Promise<T>;
resolve: (thenableOrResult?: T | PromiseLike<T>) => void;
reject: (error: any) => void;
}
Then you can define a "wait queue", which represents all the code blocks that are waiting to enter the critical section:
class WaitQueue<T> {
private deferrals: Deferral<T>[];
constructor() {
this.deferrals = [];
}
get isEmpty(): boolean {
return this.deferrals.length === 0;
}
enqueue(): Promise<T> {
const deferral = new Deferral<T>();
this.deferrals.push(deferral);
return deferral.promise;
}
dequeue(result?: T) {
const deferral = this.deferrals.shift();
deferral.resolve(result);
}
}
Finally you can define an async semaphore, as such:
export class AsyncSemaphore {
private queue: WaitQueue<void>;
private _count: number;
constructor(count: number = 0) {
this.queue = new WaitQueue<void>();
this._count = count;
}
get count(): number { return this._count; }
waitAsync(): Promise<void> {
if (this._count !== 0) {
--this._count;
return Promise.resolve();
}
return this.queue.enqueue();
}
release(value: number = 1) {
while (value !== 0 && !this.queue.isEmpty) {
this.queue.dequeue();
--value;
}
this._count += value;
}
}
Example usage:
async function performActionsInParallel() {
const semaphore = new AsyncSemaphore(10);
const listOfActions = [...];
const promises = listOfActions.map(async (action) => {
await semaphore.waitAsync();
try {
await doSomething(action);
}
finally {
semaphore.release();
}
});
const results = await Promise.all(promises);
}
This method first creates a throttler and then immediately starts all the asynchronous operations. Each asynchronous operation will first (asynchronously) wait for the semaphore to be free, then perform the action, and finally release the semaphore (allowing another one in). When all asynchronous operations have completed, all the results are retrieved.
Warning: this code is 100% completely untested. I haven't even tried it once.