Patterns async/await JavaScript à connaître
La programmation asynchrone est un pilier du JavaScript moderne, et async/await l'a rendue bien plus lisible. Pourtant, de nombreux développeurs rencontrent encore des pièges classiques : rejets de promesses non gérés, requêtes séquentielles lentes ou gestion d'erreurs confuse. Dans cet article, nous allons explorer des patterns async/await pratiques que vous pouvez appliquer immédiatement pour écrire un code plus propre et plus efficace.
1. Syntaxe de base d'async/await
Une fonction async retourne toujours une Promise. Le mot-clé await suspend l'exécution jusqu'à ce que la Promise soit résolue, puis renvoie sa valeur. Si la Promise est rejetée, une erreur est levée que vous pouvez intercepter avec try/catch.
async function fetchData() {
try {
const response = await fetch('/api/data');
const data = await response.json();
return data;
} catch (error) {
console.error('Fetch failed:', error);
}
}
C'est la base. Mais les scénarios réels nécessitent souvent des approches plus nuancées.
2. Gérer les erreurs avec élégance
L'un des plus grands avantages d'async/await est de pouvoir utiliser try/catch pour les erreurs synchrones et asynchrones. Attention toutefois aux appels await multiples dans un même bloc try : si le premier échoue, les suivants ne s'exécuteront pas.
Pour plus de granularité, vous pouvez envelopper chaque await dans son propre try/catch ou utiliser une fonction utilitaire pour éviter les blocs try/catch répétitifs.
async function getUser(id) {
try {
const user = await fetchUser(id);
return user;
} catch (error) {
console.error('Failed to fetch user:', error);
throw error;
}
}
3. Exécuter des promesses en parallèle
Attendre plusieurs promesses indépendantes séquentiellement est une erreur de performance courante. Si vous avez besoin de résultats provenant de plusieurs sources, utilisez Promise.all() pour les exécuter en parallèle.
// Séquentiel : lent
const user = await fetchUser();
const posts = await fetchPosts();
// Parallèle : rapide
const [user, posts] = await Promise.all([fetchUser(), fetchPosts()]);
Utilisez Promise.allSettled() lorsque vous voulez attendre que toutes les promesses soient réglées, qu'elles soient résolues ou rejetées. C'est utile pour collecter les résultats même si certaines échouent.
4. Exécution séquentielle quand l'ordre compte
Parfois, vous devez traiter des éléments dans l'ordre, notamment lorsque chaque étape dépend de la précédente. Dans ce cas, utilisez une boucle for...of avec await plutôt que forEach (qui n'attend pas les callbacks asynchrones).
for (const item of items) {
await processItem(item);
}
Si vous devez limiter la concurrence, envisagez des bibliothèques comme p-limit ou implémentez une file d'attente simple.
5. Éviter le piège d'async/await dans les boucles
Utiliser await dans Array.prototype.forEach est une erreur classique : forEach n'attend pas le callback asynchrone, donc la boucle se termine avant que les promesses ne soient résolues. Utilisez toujours for...of ou map avec Promise.all.
6. Async/await avec des timeouts
Pour éviter les requêtes qui restent bloquées, vous pouvez faire une course entre une promesse et un timeout.
function timeout(ms) {
return new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), ms)
);
}
await Promise.race([fetchData(), timeout(5000)]);
Ce pattern est pratique pour les appels API qui pourraient rester bloqués.
7. Générateurs asynchrones et for await...of
Les générateurs asynchrones permettent de produire un flux de valeurs de manière asynchrone. Vous pouvez les consommer avec for await...of.
async function* fetchPages() {
let page = 1;
while (true) {
const data = await fetch(`/api/items?page=${page}`);
if (data.length === 0) break;
yield data;
page++;
}
}
for await (const page of fetchPages()) {
console.log(page);
}
C'est idéal pour les API paginées ou le streaming de données.
8. Erreurs courantes et comment les éviter
- Oublier await : Appeler une fonction async sans await retourne une Promise, et les erreurs peuvent ne pas être gérées.
- Utiliser await dans des fonctions non-async : Erreur de syntaxe. Marquez toujours la fonction comme async.
- Abuser des await séquentiels : Utilisez Promise.all quand c'est possible.
- Ne pas gérer les rejets : Ajoutez toujours .catch ou try/catch.
9. Bonnes pratiques pour un code asynchrone propre
- Préférez async/await aux Promises brutes pour la lisibilité.
- Utilisez Promise.all pour les opérations indépendantes.
- Gérez les erreurs au niveau approprié.
- Gardez les fonctions async courtes et ciblées.
- Utilisez async/await avec prudence dans les boucles ; choisissez le bon pattern.
FAQ
Quelle est la différence entre async/await et les Promises ?
Async/await est du sucre syntaxique au-dessus des Promises, ce qui fait ressembler le code asynchrone à du code synchrone. Cela simplifie le chaînage et la gestion des erreurs avec try/catch.
Puis-je utiliser await dans une boucle forEach ?
Non, forEach n'attend pas les callbacks asynchrones. Utilisez plutôt for...of ou Promise.all avec map.
Comment exécuter plusieurs opérations asynchrones en parallèle ?
Utilisez Promise.all() pour les exécuter simultanément et attendre que toutes soient résolues.
Lorsque vous travaillez avec des API JSON, vous pouvez rapidement formater et valider les réponses grâce à notre JSON Formatter.