JavaScript async/await
Apprenez async/await en JavaScript : fonctions async, await, gestion des erreurs avec try/catch, exécution séquentielle vs parallèle avec Promise.all et pièges courants.
La syntaxe async/await est la façon moderne et lisible de travailler avec du code asynchrone en JavaScript. Elle repose directement sur les promesses — async/await ne les remplace pas, elle vous offre une syntaxe plus claire pour les consommer. Au lieu d'enchaîner des callbacks .then(), vous écrivez du code qui se lit de haut en bas comme du code synchrone ordinaire, tandis que le moteur gère l'attente en arrière-plan.
Ce chapitre couvre ce que retournent les fonctions async, comment await met en pause l'exécution, la gestion des erreurs avec try...catch, l'exécution des tâches de manière séquentielle ou parallèle, le await au niveau supérieur dans les modules, ainsi que les pièges qui font trébucher la plupart des développeurs.
Les fonctions async retournent toujours une promesse
Marquer une fonction async fait deux choses : cela permet d'utiliser await dans le corps de la fonction, et cela garantit que la fonction retourne une promesse. Quelle que soit la valeur que vous return, elle devient la valeur résolue de cette promesse ; si vous throw, la promesse est rejetée.
Puisque la valeur de retour est une promesse, l'appelant doit toujours utiliser await (ou .then()) pour lire la valeur réelle. Une erreur courante des débutants est de s'attendre à ce que greet() retourne directement 'Hello'.
await met en pause jusqu'à ce que la promesse se règle
L'opérateur await ne peut être utilisé qu'à l'intérieur d'une fonction async (ou au niveau supérieur d'un module — voir ci-dessous). Il met la fonction en pause jusqu'à ce que la promesse à sa droite se règle : en cas de résolution, il retourne la valeur résolue ; en cas de rejet, il lève la raison du rejet.
De façon cruciale, await ne bloque pas l'ensemble du programme. Il suspend uniquement la fonction async courante ; le reste de votre code et la boucle d'événements continuent de fonctionner.
Vous pouvez await n'importe quelle valeur, pas seulement une promesse. Les valeurs qui ne sont pas des promesses sont encapsulées et résolues immédiatement, donc await 5 retourne simplement 5.
Gestion des erreurs avec try...catch
L'un des grands avantages de async/await est que vous gérez les erreurs avec le même try...catch que vous utilisez déjà pour le code synchrone. Une promesse rejetée se transforme en exception levée au point await, que catch peut intercepter.
Si vous n'interceptez pas un rejet, il devient un rejet de promesse non géré. Pour un examen approfondi des mécanismes côté promesse, consultez la gestion des erreurs avec les promesses.
Un exemple concret : récupérer des données et signaler les échecs de façon claire.
Notez la vérification explicite de response.ok : fetch ne rejette que sur les échecs réseau, pas sur les codes d'erreur HTTP comme 404 ou 500, vous devez donc inspecter la réponse vous-même.
Exécution séquentielle vs. parallèle
C'est là que await est le plus souvent mal utilisé. Lorsque vous await des opérations l'une après l'autre, elles s'exécutent de manière séquentielle — chacune attend que la précédente se termine. C'est correct uniquement lorsqu'une tâche ultérieure dépend d'un résultat antérieur.
Séquentiel (lorsque les tâches dépendent les unes des autres)
async function pipeline() {
const user = await getUser(1); // step 1
const posts = await getPosts(user.id); // needs user.id, so must wait
return posts;
}Parallèle (lorsque les tâches sont indépendantes)
Si les tâches ne dépendent pas les unes des autres, les attendre en séquence fait perdre du temps. Démarrez-les toutes, puis attendez-les ensemble avec Promise.all :
Promise.all rejette dès que l'une de ses promesses est rejetée. Si vous souhaitez plutôt attendre toutes les promesses indépendamment de leur succès ou échec, utilisez Promise.allSettled. Consultez l'API des promesses pour la famille complète des combinateurs.
await au niveau supérieur dans les modules
Dans les modules ES (<script type="module"> ou fichiers .mjs), vous pouvez utiliser await au niveau supérieur sans l'envelopper dans une fonction async :
// data.mjs — an ES module
const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
const todo = await response.json();
export { todo };C'est pratique pour l'initialisation des modules, comme charger une configuration avant que les exports du module soient prêts. Notez qu'un module utilisant await au niveau supérieur retarde l'évaluation de tout module qui l'importe. Le await au niveau supérieur ne fonctionne que dans les modules — l'utiliser dans un script classique ou une fonction ordinaire est une erreur de syntaxe.
Pièges courants
Ne pas utiliser await dans une boucle pour des tâches indépendantes
Utiliser await dans une boucle for force les itérations à s'exécuter une par une. Si les itérations sont indépendantes, c'est inutilement lent.
Utilisez await dans une boucle uniquement lorsque chaque itération dépend réellement de la précédente, ou lorsque vous devez limiter le débit des requêtes.
Autres pièges
forEachignore les callbacks async.array.forEach(async ...)n'attend pas les promesses. Utilisez plutôt une bouclefor...ofouPromise.all(array.map(...)).- N'oubliez pas
await. Appeler une fonction async sansawait(ou.then()) retourne une promesse en attente et avale silencieusement les erreurs. Les linters signalent souvent les promesses « flottantes ». fetchne rejette pas sur les erreurs HTTP. Vérifiez toujoursresponse.ok, comme indiqué ci-dessus.
Conclusion
async/await rend le JavaScript asynchrone aussi lisible que du code synchrone tout en laissant la boucle d'événements libre. Retenez l'essentiel : toute fonction async retourne une promesse, await met en pause uniquement la fonction courante, les erreurs transitent par try...catch, et vous devriez exécuter les tâches indépendantes en parallèle avec Promise.all plutôt que de les attendre une par une. Pour renforcer les bases sous-jacentes à cette syntaxe, consultez les promesses et le chaînage de promesses.