A veces no interesa evitar que muchas peticiones asíncronas se ejecuten en paralelo, por motivos de rendimiento limite de CPU o bien por lógica del programa que debe esperar a que finalice el bloque completo antes de permitir dejar pasar a la siguiente petición,
Para ello podemos crear un semáforo, que solo permitirá la ejecución del código de 1 e 1 (o de varios si se le configura)
Caso de uso: podemos tener un servidor express que recibe simultáneamente diferentes peticiones, pero una función que necesita ser ejecutado 1 a 1 sin poder ser concurrentes, por ejemplo comprimir un archivo y enviarlo
export class Semaphore { currentRequests: any[] runningRequests: number maxConcurrentRequests: number constructor(maxConcurrentRequests = 1) { this.currentRequests = []; this.runningRequests = 0; this.maxConcurrentRequests = maxConcurrentRequests; } callFunction<T>(fnToCall: () => Promise<T>, ...args: any): Promise<T> { return new Promise((resolve, reject) => { this.currentRequests.push({ resolve, reject, fnToCall, args, }); this.tryNext(); }); } tryNext() { if (!this.currentRequests.length) { return; } else if (this.runningRequests < this.maxConcurrentRequests) { let { resolve, reject, fnToCall, args } = this.currentRequests.shift(); this.runningRequests++; let req = fnToCall(...args); req.then((res: any) => resolve(res)) .catch((err: any) => reject(err)) .finally(() => { this.runningRequests--; this.tryNext(); }); } } }
Forma de uso:
// Funcion genérica que devuelve una promesa const compress = (encoding: string, md5: string): Promise<string> => { return new Promise((resolve, reject) => { // do stuff }) } ... // Llamada a la función "callFunction" const throttler = new Semaphore(1); throttler.callFunction(() => compress('codec', 'xxx')) throttler.callFunction(() => compress('codec', 'xxx')) throttler.callFunction(() => compress('codec', 'xxx')) throttler.callFunction(() => compress('codec', 'xxx'))
Fuente original: https://medium.com/swlh/semaphores-in-javascript-e415b0d684bc