Asistí a una entrevista de codificación de NodeJS. Obtuve el siguiente código que se ejecuta de forma asíncrona desde diferentes navegadores (se supone). Nuestra solución necesita bloquear la ejecución de la función si la actualización por ID es la misma pero se llama desde un lugar diferente (por ejemplo, navegador). Y luego suelte el bloqueo para la ejecución de la siguiente solicitud.
Aquí no se deben realizar cambios para el código mencionado a continuación.
async function update(id, data) { console.log(`start --> id:${id}, data:${data}`); await randomDelay(); //update is happening here console.log(`end --> id:${id}, data:${data}`); } //============================================================================= //================= Don't change anything below =============================== //============================================================================= //---- update() is getting called from many places ---- update(1, "browser 1"); update(1, "browser 2"); //========================= Utility functions =================================== //========================= Don't change any here================================ async function sleep(ms) { return new Promise((resolve, reject) => { setTimeout(() => resolve(), ms); }); } async function randomDelay() { const randomTime = Math.round(Math.random() * 1000); return sleep(randomTime); }Esto dará una salida como la siguiente.
start --> id:1, data:browser 1 start --> id:1, data:browser 2 end --> id:1, data:browser 1 end --> id:1, data:browser 2La respuesta esperada es
start --> id:1, data:browser 1 end --> id:1, data:browser 1 start --> id:1, data:browser 2 end --> id:1, data:browser 2Tenga en cuenta los comentarios en el código "No cambie nada a continuación". ¿Cuál sería la posible solución?
Puede usar una tabla hash de colas codificada por ID para que solo los trabajos con la misma ID se ejecuten consecutivamente, de lo contrario, se ejecutan simultáneamente.
let hash = {}; class Queue { constructor() { this.isBusy = false; this.jobs = []; } push(jobFn) { return new Promise((resolve) => { this.jobs.push({ jobFn, resolve }); this.next(); }); } next() { if (this.isBusy || this.jobs.length === 0) return; this.isBusy = true; let currJob = this.jobs.shift(); return currJob.jobFn().then((data) => { currJob.resolve(data); this.isBusy = false; this.next(); }); } } async function update(id, data) { const updateFn = async () => { console.log(`start --> id:${id}, data:${data}`); await randomDelay(); //update is happening here console.log(`end --> id:${id}, data:${data}`); }; if (id in hash) { hash[id].push(updateFn); } else { hash[id] = new Queue(updateFn); hash[id].push(updateFn); } } //============================================================================= //================= Don't change anything below =============================== //============================================================================= //---- update() is getting called from many places ---- update(1, "browser 1"); update(1, "browser 2"); update(2, "browser 1"); update(2, "browser 2"); update(1, "browser 3"); update(1, "browser 4"); //========================= Utility functions =================================== //========================= Don't change any here================================ async function sleep(ms) { return new Promise((resolve, reject) => { setTimeout(() => resolve(), ms); }); } async function randomDelay() { const randomTime = Math.round(Math.random() * 1000); return sleep(randomTime); }Esta solución funciona solo para two llamadas de consecuencia con datos diferentes a los esperados, por lo que estoy trabajando para ampliarla, pero por ahora, espero que les dé una buena visión de cómo debería implementarse.
const callStack = [] async function update(id, data) { const stackLen = callStack.length; let currentIndex; if (stackLen) { let currentCall = callStack[stackLen - 1]; if (currentCall.start == true && currentCall.await == true) { setImmediate(() => update(id, data)) return; } currentIndex = stackLen - 1; if (currentCall.args[0] == id && currentCall.args[1] !== data) { if (currentCall.await === true) { currentCall.start = true; update(id, data) return; } } } else { callStack.push({ args: [...arguments], await: true }) currentIndex = 0; } console.log(`start --> id:${id}, data:${data}`); await randomDelay(); //update is happening here console.log(`end --> id:${id}, data:${data}`); callStack[currentIndex].await = false; }