forEach/map y async no van de la mano

May 07 2023
A veces, solemos pasar funciones asíncronas con funciones forEach y map. Por ejemplo: Resultado: Todos los números desde el 1.
Encabezamiento

A veces, solemos pasar funciones asíncronas con funciones forEach y map.

Por ejemplo:

[1,2,3,4,5].forEach(async (n) => setTimeout(console.log(n), 1000));

Resultado: Todos los números del 1 al 5 se imprimen uno tras otro, pero no hay un intervalo de 1 segundo después de imprimir cada línea.

¿Por qué pasó esto?

Mira esta función javascipt:

async function doubleOf(n) {
  return n*2;
}

Resultado: Esto en realidad está devolviendo una Promesa que se resuelve en un número.

Si escribimos un equivalente de TypeScript de esta función con tipos estrictos, aclarará las cosas.

El siguiente código no compilará:

async function doubleOf(n: number): number {
  return n*2;
}

La versión correcta sería:

async function doubleOf(n: number): Promise<number> {
  return n*2;
}

No se deje engañar por el azúcar sintáctico proporcionado por async-await. Si escribimos promesas puras sin usar async, la función anterior se vería así:

function doubleOf(n) {
  return new Promise((resolve) => resolve(n*2));
}

function doubleOf(n: number): Promise<number> {
  return new Promise((resolve) => resolve(n*2));
}

las funciones asíncronas devuelven promesas y no valores
  1. Tenemos doubleOfuna función que toma un número y devuelve un número. Javascript simple y antiguo.
  2. Tenemos doubleOfOldWayuna función que toma un número y devuelve una promesa que se resuelve en un número.
  3. Tenemos doubleOfNewWay, una función asíncrona que toma un número y parece que devuelve un número, pero en realidad devuelve una promesa que se resuelve en un número como la doubleOfOldWayfunción.
  4. doubleOfOldWayy doubleOfNewWaylas funciones son exactamente las mismas.
  5. Y por lo tanto, cuando tratamos de ejecutar una operación de multiplicación en los valores devueltos por doubleOfOldWayy doubleOfNewWayfunciones, el resultado fue NaN, ya que no podemos multiplicar las promesas (¡obviamente!).
  6. Para multiplicar doubleOfOldWayy doubleOfNewWay:

Volvamos a nuestro ejemplo inicial:

[1,2,3,4,5].forEach(async (n) => setTimeout(console.log(n), 1000));

La forma más correcta de implementar lo que esperamos de esta función forEach es usando un bucle for simple:

for(const number of [1,2,3,4,5]) {
    console.log(number);
    await new Promise(resolve => setTimeout(resolve, 1000)); // Sleep for "atleast" 1 second
}

[1,2,3,4,5].map(async (n) => n*2);

(5) [Promise, Promise, Promise, Promise, Promise]
0: Promise {<fulfilled>: 2}
1: Promise {<fulfilled>: 4}
2: Promise {<fulfilled>: 6}
3: Promise {<fulfilled>: 8}
4: Promise {<fulfilled>: 10}

Para obtener una lista del doble de cada número, lo que podemos hacer es:

await Promise.all([1,2,3,4,5].map(async (n) => n*2));

[2, 4, 6, 8, 10]