Avez-vous une question? (216) 97 656 803 hajjriadh@gmail.com
JavaScript

18.5 async et await

async et await. Dans cette leçon du cours JavaScript, vous apprendrez à : • déclarer une fonction async et savoir ce qu'elle renvoie ; • attendre une promesse avec await ; • gérer les erreurs avec try...

0 chapitres 0 QCM Accès gratuit
Dans cet article

À propos de ce cours

async et await. Dans cette leçon du cours JavaScript, vous apprendrez à : • déclarer une fonction async et savoir ce qu'elle renvoie ; • attendre une promesse avec await ; • gérer les erreurs avec try / catch / finally ; • choisir entre exécution séquentielle et parallèle, et éviter les pièges courants.

Chapitre 18 — La programmation asynchrone · Leçon 5 : async et await

🎯 Objectifs de la leçon
• déclarer une fonction async et savoir ce qu'elle renvoie ;
• attendre une promesse avec await;
• gérer les erreurs avec try / catch / finally;
• choisir entre exécution séquentielle et parallèle, et éviter les pièges courants.

1. Une syntaxe plus simple pour les promesses

async et await ne remplacent pas les promesses : c'est une autre façon d'écrire du code qui les utilise, qui ressemble à du code synchrone.

  • Une fonction déclarée async renvoie toujours une promesse.
  • À l'intérieur, await promesse suspend la fonction (pas le programme !) jusqu'à ce que la promesse soit réglée, puis donne sa valeur.
async function direBonjour() {
  return "Bonjour"; // la valeur est automatiquement enveloppée dans une promesse
}

const resultat = direBonjour();
console.log(resultat instanceof Promise); // true
resultat.then((texte) => console.log(texte));

function attendre(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function compteARebours() {
  for (let i = 3; i >= 1; i--) {
    console.log(i);
    await attendre(1000); // pause de la FONCTION, pas du programme
  }
  console.log("Partez !");
}

compteARebours();
console.log("Le programme continue pendant le compte à rebours");

Résultat dans la console

true
3
Le programme continue pendant le compte à rebours
Bonjour
2
1
Partez !

« Bonjour » s'affiche après la ligne « Le programme continue… » : .then est une microtâche (leçon 2). Pendant chaque await, la fonction est en pause, mais la boucle d'événements continue de tourner.

2. Le scénario complet, lu de haut en bas

Le même enchaînement qu'aux leçons 3 et 4, avec try / catch pour les erreurs :

function chargerUtilisateur(id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (id <= 0) reject(new Error("Identifiant invalide"));
      else resolve({ id, nom: "Amina" });
    }, 300);
  });
}
function chargerCommandes(utilisateur) {
  return new Promise((resolve) =>
    setTimeout(() => resolve([{ ref: "C-101" }, { ref: "C-102" }]), 300));
}
function chargerDetails(commande) {
  return new Promise((resolve) =>
    setTimeout(() => resolve({ ref: commande.ref, articles: [12.5, 7, 30] }), 300));
}
function calculerTotal(details) {
  return new Promise((resolve) =>
    setTimeout(() => resolve(details.articles.reduce((s, p) => s + p, 0)), 300));
}

// Le même enchaînement qu'aux leçons 3 et 4, lu comme du code synchrone
async function afficherTotal(id) {
  try {
    const utilisateur = await chargerUtilisateur(id);
    const commandes = await chargerCommandes(utilisateur);
    const details = await chargerDetails(commandes[0]);
    const total = await calculerTotal(details);
    console.log(`${utilisateur.nom} — ${details.ref} : ${total} DT`);
  } catch (err) {
    console.log("Erreur :", err.message);
  } finally {
    console.log(`Fin du traitement pour l'id ${id}`);
  }
}

async function main() {
  await afficherTotal(1);
  await afficherTotal(-1);
}
main();

Résultat dans la console

Amina — C-101 : 49.5 DT
Fin du traitement pour l'id 1
Erreur : Identifiant invalide
Fin du traitement pour l'id -1
Promessesasync / await
p.then(v => …)const v = await p;
.catch(e => …)try { … } catch (e) { … }
.finally(() => …)finally { … }
return Promise.resolve(v)return v; dans une fonction async
return Promise.reject(e)throw e; dans une fonction async

3. Séquentiel ou parallèle ?

await attend. Si l'on écrit trois await à la suite pour des opérations indépendantes, on perd du temps. On lance alors tout, puis on attend l'ensemble avec Promise.all.

function charger(nom, ms) {
  return new Promise((resolve) => setTimeout(() => resolve(nom), ms));
}
const secondes = (debut) => `${Math.round((Date.now() - debut) / 100) / 10} s`;

async function enSequence() {
  const debut = Date.now();
  const meteo = await charger("météo", 1000);
  const actus = await charger("actualités", 800);
  const agenda = await charger("agenda", 1200);
  console.log("Séquence :", [meteo, actus, agenda], secondes(debut));
}

async function enParallele() {
  const debut = Date.now();
  const [meteo, actus, agenda] = await Promise.all([
    charger("météo", 1000),
    charger("actualités", 800),
    charger("agenda", 1200),
  ]);
  console.log("Parallèle :", [meteo, actus, agenda], secondes(debut));
}

async function main() {
  await enSequence();
  await enParallele();
}
main();

Résultat dans la console

Séquence : [ 'météo', 'actualités', 'agenda' ] 3 s
Parallèle : [ 'météo', 'actualités', 'agenda' ] 1.2 s

Comparaison du temps total entre await séquentiel et Promise.all
Figure 5 — Opérations indépendantes : Promise.all divise ici le temps par plus de deux.

ℹ️ À retenir
Règle simple : si l'étape B a besoin du résultat de l'étape A → await l'un après l'autre. Si les étapes sont indépendantes → await Promise.all([...]).

4. Pièges fréquents

4.1 await dans forEach

forEach n'attend pas les fonctions async qu'on lui donne : la boucle se termine aussitôt.

function attendre(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}
const fichiers = ["a.txt", "b.txt", "c.txt"];

async function avecForEach() {
  fichiers.forEach(async (f) => {
    await attendre(100);
    console.log("forEach a traité", f);
  });
  console.log("forEach : fin annoncée… trop tôt !");
}

async function avecForOf() {
  for (const f of fichiers) {
    await attendre(100);
    console.log("for...of a traité", f);
  }
  console.log("for...of : fin, vraiment");
}

async function main() {
  await avecForEach();
  await attendre(500); // on laisse le forEach finir avant la démo suivante
  await avecForOf();
}
main();

Résultat dans la console

forEach : fin annoncée… trop tôt !
forEach a traité a.txt
forEach a traité b.txt
forEach a traité c.txt
for...of a traité a.txt
for...of a traité b.txt
for...of a traité c.txt
for...of : fin, vraiment

Utilisez for…of pour traiter les éléments un par un, ou Promise.all(tableau.map(async …)) pour les traiter en parallèle.

4.2 Autres erreurs classiques

  • Oublier await: la variable contient une promesse, pas la valeur (Promise { <pending> }).
  • await hors d'une fonction async: erreur de syntaxe, sauf au niveau principal d'un module (<script type="module"> ou fichier .mjs), où le top-level await est autorisé.
  • Oublier le try/catch: une promesse rejetée non gérée produit une erreur Uncaught (in promise) et, dans Node.js, peut arrêter le programme.
  • Appeler une fonction async sans attendre son résultat: ses erreurs ne remontent pas à l'appelant.
ℹ️ À retenir
• Une fonction async renvoie toujours une promesse ; return v = résolue avec v, throw e = rejetée avec e.
• await suspend la fonction, pas le programme : la boucle d'événements continue.
• Les erreurs se gèrent avec try / catch / finally, comme du code synchrone.
• Opérations dépendantes → await en séquence ; indépendantes → await Promise.all.
• Jamais d'await dans forEach: utilisez for…of ou Promise.all + map.

🛠 Activité 5 — La bibliothèque version async / await

Mission : Réécrivez une dernière fois votre scénario de réservation, puis optimisez-le.

Questions pour avancer (répondez dans votre compte rendu, puis appliquez dans votre projet) :

  1. Écrivez async function reserverLivre(titre) qui utilise await sur les trois fonctions de l'activité 4. Les fonctions de l'activité 4 doivent-elles être modifiées ? Pourquoi ?
  2. Comparez le nombre de lignes et la lisibilité des trois versions (callbacks, promesses, async/await). Laquelle préférez-vous lire ? Laquelle est la plus facile à déboguer avec des points d'arrêt ?
  3. Que vaut reserverLivre("Le Petit Prince") si on l'affiche avec console.log sans await? Comment récupérer le vrai résultat ?
  4. Ajoutez un message « Chargement… » au début et « Terminé » à la fin, affiché même en cas d'erreur. Quel bloc utilisez-vous ?
  5. Écrivez reserverPlusieurs(titres) en version séquentielle puis parallèle, et mesurez le temps avec Date.now(). Quel est le gain pour 5 livres ? Dans quel cas la version séquentielle serait-elle quand même obligatoire ?
  6. Votre collègue écrit titres.forEach(async t => await reserverLivre(t)); console.log("Tout est réservé");. Expliquez-lui le bug et proposez deux corrections.
  7. Dans un fichier .mjs, essayez d'écrire await reserverLivre("Candide") directement, hors fonction. Cela fonctionne-t-il ? Et dans un fichier .js classique ?

Je vérifie / je rends :

  • ☐ Le fichier bibliotheque-async.js.
  • ☐ Le tableau comparatif des trois versions et vos mesures de temps.

← 18.4 — Les promesses (Promise, then, catch, Promise.all)TP 18 : Simuler des traitements asynchrones →