Développement

Promise JavaScript : comprendre l’asynchrone et async/await

Par L'équipe Dico Informatique 20 Juil 2026 8 min de lecture
Promise JavaScript : comprendre l’asynchrone et async/await
Sommaire

    Une promise en JavaScript est un objet qui représente le résultat futur d’une opération asynchrone. Elle sert d’intermédiaire : au moment où le code s’exécute, la valeur n’existe pas encore, mais la promise garantit qu’elle arrivera, en cas de succès comme d’échec. C’est la brique de base de l’asynchrone moderne.

    À quoi sert une promise concrètement

    JavaScript exécute une seule chose à la fois. Quand tu demandes des données à un serveur, tu ne veux pas figer toute la page pendant la réponse. La promise résout ce problème : elle rend la main immédiatement et te prévient plus tard, quand le résultat est prêt.

    Avant les promises, on gérait ça avec des fonctions de rappel imbriquées les unes dans les autres. Trois appels enchaînés donnaient déjà un escalier de code illisible, surnommé le « callback hell ». La promise aplatit cet escalier en une chaîne linéaire, bien plus facile à relire six mois plus tard.

    On croise des promises partout : requêtes réseau avec fetch, lecture de fichiers, timers, accès à une base de données. Dès qu’une opération prend un temps indéterminé, elle renvoie généralement une promise.

    Les trois états d’une promise

    Une promise vit dans un seul de ces trois états à la fois, et une fois réglée, elle ne change plus jamais.

    • Pending (en attente) : l’opération tourne encore, aucun résultat disponible.
    • Fulfilled (tenue) : l’opération a réussi, une valeur est disponible.
    • Rejected (rompue) : l’opération a échoué, une erreur est disponible.

    Le passage de pending vers fulfilled ou rejected est définitif. C’est ce qu’on appelle une promise « settled » (réglée). Cette règle a une conséquence pratique : même si tu attaches un gestionnaire après que la promise est déjà réglée, il s’exécute quand même avec le bon résultat. Tu ne rates jamais le train.

    Créer et consommer une promise

    On construit une promise avec le constructeur Promise, qui reçoit une fonction appelée « executor ». Cette fonction expose deux leviers : resolve pour signaler le succès, reject pour signaler l’erreur.

    const commande = new Promise((resolve, reject) => {
      const stock = 3;
      if (stock > 0) {
        resolve("Commande validée");
      } else {
        reject(new Error("Rupture de stock"));
      }
    });
    
    commande
      .then(message => console.log(message))
      .catch(erreur => console.error(erreur.message));

    Côté consommation, trois méthodes suffisent. .then() récupère la valeur en cas de succès. .catch() attrape l’erreur en cas d’échec. .finally() s’exécute dans tous les cas, parfait pour masquer un spinner de chargement.

    L’enchaînement, la vraie force

    Chaque .then() renvoie une nouvelle promise. Tu peux donc les brancher en série, chaque étape recevant le résultat de la précédente. La fonction fléchée passée à .then() peut retourner une valeur simple ou une autre promise : dans les deux cas, la chaîne attend avant de continuer.

    Un seul .catch() placé en fin de chaîne intercepte l’erreur de n’importe quel maillon. Tu centralises la gestion d’erreur au lieu de la répéter à chaque étape.

    async/await, la syntaxe qui a tout changé

    Introduit avec la version ES2017 du langage, le couple async et await permet d’écrire du code asynchrone qui se lit comme du code synchrone. Sous le capot, ce sont toujours des promises : await met en pause la fonction jusqu’à ce que la promise soit réglée, puis renvoie sa valeur.

    async function recupererProfil(id) {
      try {
        const reponse = await fetch(`/api/users/${id}`);
        const profil = await reponse.json();
        return profil;
      } catch (erreur) {
        console.error("Échec du chargement :", erreur.message);
      }
    }

    Ici, chaque await remplace un .then(), et le bloc try/catch remplace le .catch(). Le résultat est plus plat, plus proche de la lecture naturelle. Toute fonction marquée async renvoie automatiquement une promise, même si tu retournes une valeur brute.

    Un piège classique : oublier le await. Sans lui, tu manipules l’objet promise lui-même au lieu de sa valeur, et tu obtiens un mystérieux [object Promise] dans la console.

    Combiner plusieurs promises

    Quand tu lances plusieurs opérations indépendantes, tu n’as pas à les attendre une par une. Les méthodes statiques de Promise orchestrent des groupes entiers.

    • Promise.all attend que toutes réussissent et renvoie un tableau de résultats. Si une seule échoue, l’ensemble échoue.
    • Promise.allSettled attend que toutes se règlent, succès ou échec, et te rend le détail de chacune sans t’interrompre.
    • Promise.race renvoie le résultat de la première réglée, utile pour poser un délai maximal sur un appel.
    • Promise.any renvoie la première qui réussit, en ignorant les échecs tant qu’il en reste une en course.

    Lancer trois appels vers une API en parallèle avec Promise.all divise le temps d’attente total. Trois requêtes de 200 ms enchaînées prennent 600 ms ; lancées ensemble, elles tiennent dans 200 ms. Le gain est direct sur la vitesse ressentie de ton interface.

    Quand s’exécute vraiment une promise

    Le résultat d’une promise ne passe jamais avant le code synchrone en cours. Le moteur JavaScript range les gestionnaires .then() dans une file spéciale, la file de microtâches, vidée seulement quand la pile d’appel est libre. D’où un ordre d’exécution qui surprend souvent les débutants.

    console.log("1");
    Promise.resolve().then(() => console.log("2"));
    console.log("3");
    // Affiche 1, puis 3, puis 2

    Le « 2 » sort en dernier parce que son gestionnaire attend la fin du script synchrone. Cette file de microtâches a priorité sur les timers classiques comme setTimeout, qui passent, eux, dans une file de macrotâches traitée plus tard. Comprendre cet ordre évite des bugs de séquence difficiles à traquer, surtout quand tu mélanges promises et timers dans une même fonction.

    Retiens la règle simple : ton code linéaire s’exécute d’abord en entier, puis les promises réglées prennent le relais. C’est ce qui rend l’asynchrone prévisible malgré son apparence désordonnée.

    Les erreurs courantes à éviter

    La première : avaler les rejets. Une promise rejetée sans .catch() ni try/catch produit un « unhandled rejection », et l’erreur file silencieusement. Attache toujours un gestionnaire d’échec.

    La deuxième : enchaîner du séquentiel là où le parallèle suffirait. Multiplier les await l’un après l’autre alors que les tâches sont indépendantes ralentit ton code sans raison. Regroupe-les.

    La troisième : mélanger .then() et async/await dans la même fonction. Ça marche, mais ça brouille la lecture. Choisis un style et tiens-t’y sur une fonction donnée. Cette logique s’inscrit dans la programmation événementielle propre à JavaScript, où le moteur traite les résultats dès qu’ils sont disponibles.

    FAQ

    Quelle différence entre une promise et un callback ?

    Un callback est une fonction que tu passes pour être rappelée plus tard. Une promise est un objet qui représente le résultat futur et expose des méthodes standard (.then, .catch). La promise s’enchaîne proprement et centralise les erreurs, là où les callbacks imbriqués deviennent vite illisibles. La promise n’a pas remplacé le callback : elle l’encapsule dans une interface plus lisible.

    async/await remplace-t-il complètement les promises ?

    Non. async/await est une syntaxe posée par-dessus les promises, pas un système à part. Chaque fonction async renvoie une promise, et await attend une promise. Tu gardes besoin des promises directes pour des cas comme Promise.all, où tu combines plusieurs opérations en parallèle plutôt que de les attendre en file.

    Une promise peut-elle changer d’état plusieurs fois ?

    Non. Une promise passe une seule fois de l’état pending vers fulfilled ou rejected, puis se fige. Les appels suivants à resolve ou reject dans l’executor sont ignorés. Cette immuabilité garantit qu’un même résultat est distribué à tous les gestionnaires attachés, même ceux ajoutés après coup.

    En bref

    La promise transforme l’asynchrone en code lisible : trois états, une chaîne linéaire, une gestion d’erreur centralisée. Maîtrise d’abord .then et .catch pour comprendre le mécanisme, puis bascule sur async/await au quotidien. Prochaine étape : réécris ta dernière fonction bourrée de callbacks imbriqués en une seule chaîne de promises.