import { Future } from './future'; class AsyncWaitQueue { // Code from StephenCleary https://gist.github.com/StephenCleary/ba50b2da419c03b9cba1d20cb4654d5e private _queue: Future[] = []; public get length(): number { return this._queue.length; } public enqueue(): Promise { const future = new Future(); this._queue.push(future); return future.promise; } public dequeue(value: T): void { const future = this._queue.shift(); if (future) { future.resolve(value); } } } /** * Semaphore allows you to limit the amount of async calls happening between `enter()` and `exit()` * * This can be useful when limiting the number of http calls, browser api calls, etc either for performance or to work * around browser limitations like max Image.decode() calls in chromium being 256. */ export class Semaphore { private _waitQueue = new AsyncWaitQueue(); constructor(private _count: number) {} public get count() { return this._count; } public get waiting() { return this._waitQueue.length; } // eslint-disable-next-line require-await public async enter() { if (this._count !== 0) { this._count--; return Promise.resolve(); } return this._waitQueue.enqueue(); } public exit(count: number = 1) { if (count === 0) { return; } while (count !== 0 && this._waitQueue.length !== 0) { this._waitQueue.dequeue(null); count--; } this._count += count; } }