Como sabemos, javascript por definición es asíncrono, esto significa que podemos llamar a una función y sin esperar que finalice, continuar con la siguiente instrucción, a esto le llamamos "ASYNC" o "no bloqueante" pero también podemos hacer que la ejecución de nuestro código "espere" a que esta finalice una función para poder continuar con la siguiente instrucción, a esto le llamamos "SYNC" o "bloqueante".
El término asíncrono se refiere al concepto de que más de una cosa ocurre al mismo tiempo o en paralelo, o múltiples cosas relacionadas ocurren sin esperar a que la previa se haya completado.
RefEl término síncrono se refiere al concepto de que las cosas ocurren de manera secuencial o lineal, una después de la anterior, las cosas relacionadas deben esperar a que la previa se haya completado.
Dicho lo anterior, nuestros LOOPS de toda la vida pueden tener comportamiento SYNC o ASYNC según el caso de uso que estemos resolviendo.
Vamos a ver ambos ejemplos y cómo abordarlos
Entorno
Para el ejemplo nos ayudaremos de dos funciones dummy que simularan operaciones cualquiera.
dummyTask
Una función de ejemplo que tardara 3 segundos en ejecutarse, simulando una operación/función cualquieradummyFunctionAsync
Función que internamente llama 3 veces adummyTask
de forma lineal (no async) que tardara 9 segundos en terminar su ejecución (3dummyTask
de 3seg cada una), retornando una promesa después de finalizadas las tres llamadas.
const dummyTask = (taskNumber) => {
//Does many things
return new Promise((resolve, reject) =>
setTimeout(() => {
console.log(`\t\tTask ${taskNumber} finished after 3 second.`);
resolve(true);
}, 3000)
);
};
const dummyFunctionAsync = async (iterationNumber) => {
console.log(`\tdummyFunctionAsync called ${iterationNumber} started`);
//Does many things
await dummyTask(1);
await dummyTask(2);
await dummyTask(3);
console.log(`\tdummyFunctionAsync called ${iterationNumber} finished\n`);
};
Ciclo SYNC (Lineal)
Las llamadas se ejecutan 1 detrás de la otra, esperando siempre que finalice una función antes de continuar con la siguiente.
const items = [1, 2, 3];
// Ejecución en linea de las llamadas a dummyFunctionAsync
const mainFunctionWithForOf = async () => {
console.log("forOf: started");
let index = 0;
for (const item of items) {
await dummyFunctionAsync(index);
index++;
}
console.log("forOf: ending");
};
Ejemplo de la salida en consola
forOf: started
dummyFunctionAsync called 0 started
Task 1 finished after 3 second.
Task 2 finished after 3 second.
Task 3 finished after 3 second.
dummyFunctionAsync called 0 finished
dummyFunctionAsync called 1 started
Task 1 finished after 3 second.
Task 2 finished after 3 second.
Task 3 finished after 3 second.
dummyFunctionAsync called 1 finished
dummyFunctionAsync called 2 started
Task 1 finished after 3 second.
Task 2 finished after 3 second.
Task 3 finished after 3 second.
dummyFunctionAsync called 2 finished
forOf: ending
Ciclo ASYNC (En paralelo)
forEach
Las llamadas se ejecutan de forma simultanea
const items = [1, 2, 3];
// Ejecución en paralelo de las llamadas a dummyFunctionAsync
const mainFunctionWithForEach = () => {
console.log("forEach: started");
items.forEach(async (item, index) => {
await dummyFunctionAsync(index);
});
console.log("forEach: ending");
};
Ejemplo de la salida en consola
forEach: started
dummyFunctionAsync called 0 started
dummyFunctionAsync called 1 started
dummyFunctionAsync called 2 started
forEach: ending
Task 1 finished after 3 second.
Task 1 finished after 3 second.
Task 1 finished after 3 second.
Task 2 finished after 3 second.
Task 2 finished after 3 second.
Task 2 finished after 3 second.
Task 3 finished after 3 second.
dummyFunctionAsync called 0 finished
Task 3 finished after 3 second.
dummyFunctionAsync called 1 finished
Task 3 finished after 3 second.
dummyFunctionAsync called 2 finished
map
Las llamadas se ejecutan de forma simultanea, pero el map retorna las promesas que son almacenadas en un array, y podemos usar este array para "esperar" a que terminen todas las promesas antes de continuar
const items = [1, 2, 3];
// Ejecución en paralelo de las llamadas a dummyFunctionAsync
const mainFunctionWithMap = async () => {
console.log("MAP: started");
const promises = items.map(async (item, index) => {
await dummyFunctionAsync(index);
});
console.log("MAP: ending");
console.log("waiting for all the promises to end");
const response = await Promise.all(promises);
console.log("all promises finished");
};
Ejemplo de la salida en consola
MAP: started
dummyFunctionAsync called 0 started
dummyFunctionAsync called 1 started
dummyFunctionAsync called 2 started
MAP: ending
waiting for all the promises to end
Task 1 finished after 3 second.
Task 1 finished after 3 second.
Task 1 finished after 3 second.
Task 2 finished after 3 second.
Task 2 finished after 3 second.
Task 2 finished after 3 second.
Task 3 finished after 3 second.
dummyFunctionAsync called 0 finished
Task 3 finished after 3 second.
dummyFunctionAsync called 1 finished
Task 3 finished after 3 second.
dummyFunctionAsync called 2 finished
all promises finished