Charger des données, lire un fichier, appeler une API : ces opérations prennent du temps, et JavaScript refuse d'attendre les bras croisés. Pendant des années, la réponse a été d'empiler des fonctions de rappel, avec le code écrit dans un ordre qui n'était plus celui de la lecture.
async a réglé ce problème de présentation. Le code redevient une suite d'instructions lues de haut en bas, alors que le moteur, lui, continue de travailler autrement.
Définition
async se place devant une fonction et change deux choses : la fonction renvoie désormais toujours une Promise, et le mot-clé await devient utilisable dans son corps.
async function chargeProfil(id) {
const reponse = await fetch('/api/profil/' + id);
return reponse.json();
}
chargeProfil(7).then(profil => console.log(profil));La ligne await ressemble à un appel ordinaire, alors qu'elle met la fonction en pause et rend la main au reste du programme jusqu'à l'arrivée de la réponse.
Ce qu'une fonction async renvoie toujours
C'est la règle qui surprend le plus : quoi que vous écriviez après return, l'appel produit une promesse.
| Déclaration | Ce que l'appel produit |
|---|---|
function f() { return 42 } | 42 |
async function f() { return 42 } | une promesse tenue avec 42 |
async function f() { throw ... } | une promesse rejetée |
async function chiffre() {
return 42;
}
console.log(chiffre()); // Promise { 42 }
chiffre().then(v => console.log(v)); // 42Une valeur qui s'affiche comme Promise dans la console vient presque toujours de là : la fonction a été appelée sans await devant.
Les quatre endroits où on l'écrit
- Devant une déclaration :
async function charge() {}. - Devant une fonction fléchée :
const charge = async () => {}. - Devant une méthode, dans une class ou un objet :
async charge() {}. - Devant une fonction appelée aussitôt :
(async () => { ... })(), utile pour ouvrir une zone asynchrone dans un fichier qui ne l'est pas.
Un throw dans une fonction async ne remonte pas comme une erreur classique : elle rejette la promesse renvoyée. Elle se rattrape donc avec .catch(), ou avec un try autour de l'appel, à condition que celui-ci soit précédé d'un await.
Questions fréquentes
Faut-il mettre async sur toutes les fonctions ?
Non. Une fonction async qui ne contient aucun await n'apporte rien : elle emballe simplement son résultat dans une promesse, obligeant tous ses appelants à s'adapter. Réservez le mot-clé aux fonctions qui attendent réellement quelque chose.
Est-ce que async ralentit le code ?
Le coût propre du mot-clé est négligeable. Ce qui coûte, c'est la manière dont vous enchaînez les attentes : trois appels attendus l'un après l'autre prennent la somme de leurs durées, alors que les lancer ensemble avec Promise.all() prend celle du plus lent. Le vrai levier de performance est là, jamais dans le mot-clé lui-même.
Peut-on utiliser await sans async ?
Oui, au niveau supérieur d'un module, où await s'écrit directement sans englober le code dans une fonction. Partout ailleurs, notamment dans un script classique, il faut une fonction async. Notre formation JavaScript reprend cet enchaînement depuis les promesses, l'étape que beaucoup sautent avant de se heurter à ces messages d'erreur.