Async/await dans forEach : comment concilier asynchronisme et boucles

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Pourquoi votre async/await reste silencieux dans forEach ? Comprenons d'un premier coup d'œil

\n\n

Avez-vous déjà écrit un code comme celui-ci, en espérant qu'il exécute les opérations asynchrones dans l'ordre ?

\n\n
const users = ['alice', 'bob', 'charlie'];\n\nusers.forEach(async (user) => {\n  const data = await fetch(`https://api.example.com/users/${user}`);\n  console.log(await data.json());\n});\n\nconsole.log('Terminé !');
\n\n

Et obtenu "Terminé !" avant même que les données ne soient chargées ? Cela vous dit quelque chose ? Vous n'êtes pas seul. C'est l'une des questions les plus populaires sur Stack Overflow avec des milliers de vues. Le problème est que forEach est une méthode synchrone. Elle exécute simplement les callbacks, mais n'attend pas leur achèvement. Vos fonctions async démarrent, mais forEach file déjà, comme un sprinteur qui se moque de l'arrivée des autres.

\n\n

Dans cet article, nous ne montrerons pas seulement comment résoudre ce problème. Nous analyserons pourquoi cela se produit, et vous donnerons trois solutions fonctionnelles avec des exemples. Vous apprendrez quand utiliser Promise.all, et quand utiliser le bon vieux for...of. C'est parti !

\n\n

Anatomie du problème : forEach et les promesses

\n\n

Regardons sous le capot. La méthode forEach en JavaScript est conçue ainsi :

\n\n
Array.prototype.forEach = function(callback) {\n  for (let i = 0; i < this.length; i++) {\n    callback(this[i], i, this);\n  }\n};
\n\n

Vous voyez ? Elle appelle simplement callback pour chaque élément, mais ne retourne rien. Même si le callback retourne une Promise, forEach l'ignore. Elle n'attend pas, ne collecte pas les résultats — elle exécute et oublie.

\n\n

Lorsque vous écrivez async (user) => { ... }, votre fonction retourne une Promise. Mais forEach ne sait pas quoi en faire. Résultat : toutes les requêtes sont lancées en parallèle (s'il s'agit de fetch), mais vous ne pouvez pas attendre leur achèvement. Le code après forEach s'exécute instantanément.

\n\n

Exemple concret : à quoi cela ressemble en pratique

\n\n

Imaginez que vous traitez un tableau d'ID de produits et que pour chacun, vous devez obtenir le prix depuis une base de données :

\n\n
const productIds = [101, 102, 103];\n\nasync function getPrice(id) {\n  // Simulation d'une requête à la BDD\n  return new Promise(resolve => {\n    setTimeout(() => {\n      resolve(`Prix pour ${id}: $${Math.floor(Math.random() * 100)}`);\n    }, 1000);\n  });\n}\n\nproductIds.forEach(async (id) => {\n  const price = await getPrice(id);\n  console.log(price);\n});\n\nconsole.log('Tous les prix sont chargés !'); // S'exécute immédiatement, pas après 3 secondes
\n\n

Résultat dans la console :

\n
Tous les prix sont chargés !\nPrix pour 101: $42\nPrix pour 102: $78\nPrix pour 103: $15
\n\n

Vous en conviendrez, ce n'est pas ce à quoi nous nous attendions. Bonne nouvelle : il existe des solutions simples et élégantes.

\n\n

Solution n°1 : for...of — exécution séquentielle

\n\n

La façon la plus simple de faire fonctionner async/await dans une boucle est d'utiliser for...of. Contrairement à forEach, for...of fonctionne avec async/await nativement, car vous contrôlez le flux d'exécution à l'intérieur du bloc.

\n\n
const productIds = [101, 102, 103];\n\nasync function getPrice(id) {\n  return new Promise(resolve => {\n    setTimeout(() => {\n      resolve(`Prix pour ${id}: $${Math.floor(Math.random() * 100)}`);\n    }, 1000);\n  });\n}\n\nasync function loadAllPrices() {\n  for (const id of productIds) {\n    const price = await getPrice(id);\n    console.log(price);\n  }\n  console.log('Tous les prix sont chargés !');\n}\n\nloadAllPrices();
\n\n

Maintenant, le résultat sera :

\n
Prix pour 101: $42\n(après 1 seconde)\nPrix pour 102: $78\n(après 1 seconde)\nPrix pour 103: $15\n(après 1 seconde)\nTous les prix sont chargés !
\n\n

Avantages : simplicité, lisibilité, ordre prévisible.

\n

Inconvénients : lent — chaque requête attend la fin de la p

Blogs

Book Recommendations