forEach/map et async ne vont pas de pair

May 07 2023
Parfois, nous avons souvent tendance à passer des fonctions asynchrones avec les fonctions forEach et map. Par exemple : résultat : tous les nombres à partir de 1.
Entête

Parfois, nous avons souvent tendance à passer des fonctions asynchrones avec les fonctions forEach et map.

Par exemple :

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

Résultat : Tous les nombres de 1 à 5 sont imprimés les uns après les autres, mais il n'y a pas d'intervalle de 1 seconde après l'impression de chaque ligne.

Pourquoi cela arrive-t-il?

Regardez cette fonction javascipt :

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

Résultat : Il s'agit en fait de renvoyer une promesse qui se résout en un nombre.

Si nous écrivons un équivalent Typescript de cette fonction avec des types stricts, cela clarifiera les choses.

Le code suivant ne compilera pas :

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

La bonne version serait :

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

Ne vous laissez pas berner par le sucre syntaxique fourni par async-wait. Si nous écrivions des promesses pures sans utiliser async, la fonction ci-dessus ressemblerait à :

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

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

les fonctions asynchrones renvoient des promesses et non des valeurs
  1. Nous avons doubleOfune fonction qui prend un nombre et renvoie un nombre. Vieux javascript simple.
  2. Nous avons doubleOfOldWayune fonction qui prend un nombre et renvoie une promesse qui se résout en un nombre.
  3. Nous avons doubleOfNewWay, une fonction asynchrone qui prend un nombre et semble renvoyer un nombre, mais elle renvoie en fait une promesse qui se résout en un nombre tout comme doubleOfOldWayla fonction.
  4. doubleOfOldWayet doubleOfNewWayles fonctions sont exactement les mêmes.
  5. Et par conséquent, lorsque nous essayons d'exécuter une opération de multiplication sur les valeurs renvoyées par les fonctions doubleOfOldWayet doubleOfNewWay, le résultat était NaN, car nous ne pouvons pas multiplier les promesses (évidemment !).
  6. Multiplier doubleOfOldWayet doubleOfNewWay:

Revenons donc à notre exemple initial :

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

La manière la plus correcte d'implémenter ce que nous attendons de cette fonction forEach consiste à utiliser une simple boucle for :

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}

Pour obtenir une liste des doubles de chaque nombre, ce que nous pouvons faire est :

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

[2, 4, 6, 8, 10]