// js · Web Platform Advent #3
async/await en JavaScript : guide pratique
Comment fonctionnent async et await, leur lien avec les promesses, la gestion d'erreurs avec try/catch, await dans les boucles et le piège série-vs-parallèle — avec des exemples exécutables.
Les mots-clés async et await sont une syntaxe au-dessus des promesses. Ils permettent d'écrire du code asynchrone qui se lit de haut en bas comme du code synchrone, tout en restant non bloquant en coulisse. Si vous connaissez les promesses, vous connaissez déjà async/await — il supprime simplement le cérémonial des .then().
Les bases
Marquez une fonction async et vous pouvez utiliser await à l'intérieur. await met cette fonction en pause jusqu'à ce que la promesse soit réglée, puis vous donne la valeur résolue :
async function getUser(id) {
const res = await fetch('/api/user/' + id);
const user = await res.json();
return user.name;
}
getUser(42).then((name) => console.log(name)); Deux choses à retenir : une fonction async renvoie toujours une promesse (ce que vous renvoyez est emballé dedans), et await ne fonctionne qu'à l'intérieur d'une fonction async — sauf au niveau racine d'un module ES, où le top-level await est autorisé.
async/await vs promesses
C'est la même mécanique. Cette chaîne de promesses…
fetch('/api/user')
.then((res) => res.json())
.then((user) => console.log(user.name)); …est exactement équivalente à cette version async, que la plupart trouvent plus simple à lire et à déboguer :
const res = await fetch('/api/user');
const user = await res.json();
console.log(user.name); On peut les mélanger librement — await n'importe quelle promesse, y compris le résultat de Promise.all().
Gestion d'erreurs avec try/catch
Avec async/await, on attrape les promesses rompues avec un try/catch ordinaire, la même construction que pour les erreurs synchrones :
async function loadUser(id) {
try {
const res = await fetch('/api/user/' + id);
if (!res.ok) throw new Error('HTTP ' + res.status);
return await res.json();
} catch (err) {
console.error('Impossible de charger l’utilisateur :', err.message);
return null;
} finally {
console.log('loadUser terminé');
}
} Un await rompu lève une exception à cette ligne, donc le bloc catch s'exécute. Le bloc finally s'exécute qu'il y ait eu une erreur ou non.
Le grand piège : await en série vs en parallèle
L'erreur async/await la plus courante est d'attendre des tâches indépendantes l'une après l'autre. Ceci s'exécute en série — la deuxième requête ne démarre qu'une fois la première terminée :
// Lent : temps total = a + b + c
const a = await fetchA();
const b = await fetchB();
const c = await fetchC(); Si les tâches ne dépendent pas l'une de l'autre, lancez-les toutes d'abord, puis attendez ensemble avec Promise.all. Elles s'exécutent alors en parallèle :
// Rapide : temps total ≈ la plus lente
const [a, b, c] = await Promise.all([fetchA(), fetchB(), fetchC()]); await dans les boucles
Une simple boucle for avec await traite les éléments un par un, ce qui est correct quand chaque étape dépend de la précédente ou qu'il faut éviter de surcharger un serveur :
for (const id of ids) {
const item = await fetchItem(id); // séquentiel — l’un après l’autre
console.log(item);
} Pour les traiter en parallèle, transformez en tableau de promesses et await Promise.all. Attention : forEach n'attend pas les callbacks async — utilisez map + Promise.all à la place :
const items = await Promise.all(ids.map((id) => fetchItem(id))); Aide-mémoire
| Objectif | Pattern |
|---|---|
| Attendre une promesse | const x = await p; |
| Gérer les erreurs | try { await ... } catch (e) { ... } |
| Tâches indépendantes en parallèle | await Promise.all([...]) |
| Boucle séquentielle | for...of avec await |
| Boucle concurrente | map → Promise.all |
async/await ne remplace pas les promesses — il les rend lisibles. Gardez Promise.all à portée pour le parallélisme, utilisez try/catch pour les erreurs, et méfiez-vous de l'await en série accidentel : vous aurez alors le JavaScript asynchrone bien en main.