Aller au contenu principal
LaPolaris lance ses formations, profitez de -40% jusqu'au 30 juin ! En savoir plus

JavaScript asynchrone : comprendre enfin les Promises, async/await et les erreurs courantes

L'asynchrone en JavaScript est le concept qui bloque le plus les débutants. Callbacks, Promises, async/await — ce guide démêle tout avec des exemples concrets et explique pourquoi ça marche comme ça.

Guides & tutoriels ·
Adel LATIBI
· ·
JavaScript asynchrone : comprendre enfin les Promises, async/await et les erreurs courantes
Il y a un moment précis dans l'apprentissage de JavaScript où beaucoup de gens dérochent. Ce moment, c'est quand ils rencontrent l'asynchrone. Les callbacks imbriqués, les Promises qui semblent magiques, le async/await qui cache ce qui se passe vraiment — tout ça crée une zone de brouillard qui dure parfois des mois.
Cet article est une tentative de dissiper ce brouillard définitivement.
Pourquoi JavaScript est asynchrone
JavaScript s'exécute dans un seul thread. Il ne peut faire qu'une seule chose à la fois. Mais certaines opérations prennent du temps : une requête réseau, une lecture de fichier, un timer. Si JavaScript attendait bêtement que ces opérations se terminent, la page serait figée pendant tout ce temps.
L'asynchrone, c'est la solution à ce problème : JavaScript lance une opération longue, continue à exécuter le reste du code, puis revient traiter le résultat quand l'opération est terminée. C'est l'event loop, et c'est le cœur du fonctionnement de JavaScript.
L'évolution : callbacks → Promises → async/await
Étape 1 : les callbacks (l'ancêtre)
Un callback, c'est une fonction qu'on passe en paramètre à une autre fonction, pour qu'elle soit appelée une fois l'opération terminée.
// Exemple avec setTimeout
console.log("Début");

setTimeout(function() {
  console.log("Ceci s'affiche après 2 secondes");
}, 2000);

console.log("Fin");

// Affiche : "Début", "Fin", puis "Ceci s'affiche après 2 secondes"
Le problème des callbacks arrive quand on les imbrique :
getData(function(result1) {
  getMoreData(result1, function(result2) {
    getEvenMoreData(result2, function(result3) {
      // On est en enfer
    });
  });
});
C'est le callback hell : illisible, difficile à déboguer, cauchemar à maintenir.
Étape 2 : les Promises (la révolution)
Une Promise est un objet qui représente une valeur qui sera disponible dans le futur. Elle peut être dans trois états : pending (en attente), fulfilled (résolue avec succès), ou rejected (échouée).
fetch('https://api.example.com/users')
  .then(response => response.json())
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.error("Erreur :", error);
  });
C'est déjà beaucoup plus lisible. Les opérations sont chaînées horizontalement plutôt qu'imbriquées. Le .catch() à la fin attrape toutes les erreurs de la chaîne.
Étape 3 : async/await (la syntaxe moderne)
async/await n'est pas une nouvelle fonctionnalité — c'est du sucre syntaxique au-dessus des Promises. Ça rend le code asynchrone lisible comme du code synchrone.
async function getUsers() {
  try {
    const response = await fetch('https://api.example.com/users');
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error("Erreur :", error);
  }
}

getUsers();
Le mot-clé await dit à JavaScript : "attends que cette Promise soit résolue avant de continuer". Le async devant la fonction dit : "cette fonction retourne une Promise".
Les erreurs classiques
Erreur 1 : oublier await
// ❌ Mauvais : data est une Promise, pas les données
async function getUser() {
  const data = fetch('/api/user').json();
  console.log(data); // Promise { pending }
}

// ✅ Correct
async function getUser() {
  const response = await fetch('/api/user');
  const data = await response.json();
  console.log(data); // Les données réelles
}
Erreur 2 : await dans une boucle for...of vs Promise.all
const userIds = [1, 2, 3, 4, 5];

// ❌ Lent : attend chaque requête avant de lancer la suivante
for (const id of userIds) {
  const user = await getUser(id);
  console.log(user);
}

// ✅ Rapide : lance toutes les requêtes en parallèle
const users = await Promise.all(userIds.map(id => getUser(id)));
console.log(users);
La différence de performance peut être énorme : 5 requêtes de 200ms en séquence = 1 seconde. En parallèle avec Promise.all = 200ms.
Erreur 3 : ne pas gérer les erreurs
// ❌ Si fetch échoue, l'erreur remonte silencieusement
async function getData() {
  const response = await fetch('/api/data');
  return response.json();
}

// ✅ Toujours entourer d'un try/catch
async function getData() {
  try {
    const response = await fetch('/api/data');
    if (!response.ok) {
      throw new Error(`HTTP error: ${response.status}`);
    }
    return await response.json();
  } catch (error) {
    console.error('Erreur lors de la récupération des données:', error);
    throw error; // Re-lancer pour que l'appelant puisse gérer
  }
}
Erreur 4 : confondre async avec "en parallèle"
Un code async/await s'exécute toujours de façon séquentielle à l'intérieur de la fonction. await suspend l'exécution de la fonction en attendant la Promise. Pour du vrai parallélisme, utilisez Promise.all().
Cas d'usage concrets
Appel API avec gestion d'état (style React)
import { useState, useEffect } from 'react';

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function fetchUser() {
      try {
        const response = await fetch(`/api/users/${userId}`);
        if (!response.ok) throw new Error('Utilisateur introuvable');
        const data = await response.json();
        setUser(data);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    }

    fetchUser();
  }, [userId]);

  if (loading) return <p>Chargement...</p>;
  if (error) return <p>Erreur : {error}</p>;
  return <div>{user.name}</div>;
}
Ce qu'il faut retenir
  • async/await est du sucre syntaxique au-dessus des Promises — comprendre les Promises, c'est comprendre async/await
  • Tout await dans une boucle séquentielle est potentiellement un problème de performance — pensez à Promise.all
  • Gérez toujours les erreurs avec try/catch et vérifiez response.ok après un fetch
  • Une fonction async retourne toujours une Promise — même si vous retournez une valeur primitive
L'asynchrone JavaScript est l'un de ces sujets où "lire un article" ne suffit pas. Il faut coder, se tromper, déboguer, et recommencer. Mais une fois que le déclic se fait, vous ne reverrez plus jamais un .then() avec les mêmes yeux.