`await` de niveau supérieur
await
de niveau supérieur permet aux développeurs d'utiliser le mot-clé await
en dehors des fonctions asynchrones. Il agit comme une grande fonction asynchrone, obligeant d'autres modules qui les import
à attendre avant de commencer à évaluer leur corps.
L'ancien comportement
Lorsque async
/await
a été introduit pour la première fois, tenter d'utiliser un await
en dehors d'une fonction async
entraînait une SyntaxError
. De nombreux développeurs utilisaient des expressions de fonction async immédiatement invoquées afin d'accéder à cette fonctionnalité.
await Promise.resolve(console.log('🎉'));
// → SyntaxError: await is only valid in async function
(async function() {
await Promise.resolve(console.log('🎉'));
// → 🎉
}());
Le nouveau comportement
Avec await
de niveau supérieur, le code ci-dessus fonctionne comme prévu dans les modules :
await Promise.resolve(console.log('🎉'));
// → 🎉
Remarque : await
de niveau supérieur fonctionne uniquement au niveau supérieur des modules. Il n'y a pas de prise en charge pour les scripts classiques ou les fonctions non asynchrones.
Cas d'utilisation
Ces cas d'utilisation sont tirés du dépôt de proposition de spécification.
Dépendances dynamiques
const strings = await import(`/i18n/${navigator.language}`);
Cela permet aux modules d'utiliser des valeurs d'exécution pour déterminer les dépendances. Cela est utile pour des cas comme les scissions développement/production, l'internationalisation, les scissions d'environnement, etc.
Initialisation des ressources
const connection = await dbConnector();
Cela permet aux modules de représenter des ressources et également de produire des erreurs dans les cas où le module ne peut pas être utilisé.
Alternatives pour les dépendances
L'exemple suivant tente de charger une bibliothèque JavaScript depuis le CDN A, et revient au CDN B en cas d'échec :
let jQuery;
try {
jQuery = await import('https://cdn-a.example.com/jQuery');
} catch {
jQuery = await import('https://cdn-b.example.com/jQuery');
}
Ordre d'exécution des modules
L'une des plus grandes modifications de JavaScript avec await
de niveau supérieur est l'ordre d'exécution des modules dans votre graphe. Le moteur JavaScript exécute les modules en traversée en post-ordre : en commençant par le sous-arbre le plus à gauche de votre graphe de modules, les modules sont évalués, leurs liaisons sont exportées, et leurs frères et sœurs sont exécutés, suivis par leurs parents. Cet algorithme s'exécute de manière récursive jusqu'à l'exécution de la racine de votre graphe de modules.
Avant await
de niveau supérieur, cet ordre était toujours synchrone et déterministe : entre plusieurs exécutions de votre code, votre graphe était garanti de s'exécuter dans le même ordre. Une fois que await
de niveau supérieur est implémenté, cette même garantie existe, mais seulement tant que vous n'utilisez pas await
de niveau supérieur.
Voici ce qui se passe lorsque vous utilisez await
de niveau supérieur dans un module :
- L'exécution du module en cours est différée jusqu'à ce que la promesse attendue soit résolue.
- L'exécution du module parent est différée jusqu'à ce que le module enfant qui a appelé
await
, et tous ses frères et sœurs, exportent des liaisons. - Les modules frères, ainsi que les frères et sœurs des modules parents, peuvent continuer à s'exécuter dans le même ordre synchrone — en supposant qu'il n'y a pas de cycles ou d'autres promesses
await
dans le graphe. - Le module qui a appelé
await
reprend son exécution après la résolution de la promesseawait
. - Le module parent et les arbres suivants continuent de s'exécuter dans un ordre synchrone tant qu'il n'y a pas d'autres promesses
await
.
Cela ne fonctionne-t-il pas déjà dans DevTools ?
En effet, oui ! Le REPL dans Chrome DevTools, Node.js, et Safari Web Inspector prennent en charge await
de niveau supérieur depuis un certain temps maintenant. Cependant, cette fonctionnalité n'était pas standard et limitée au REPL ! Elle est distincte de la proposition await
de niveau supérieur, qui fait partie de la spécification du langage et ne s'applique qu'aux modules. Pour tester du code en production s'appuyant sur await
de niveau supérieur de manière conforme à la sémantique de la proposition de spécification, assurez-vous de tester dans votre application réelle, et pas uniquement dans DevTools ou le REPL de Node.js !
await
de niveau supérieur n'est-il pas problématique ?
Peut-être avez-vous vu le célèbre gist de Rich Harris qui mettait initialement en lumière un certain nombre de préoccupations concernant l'await
de niveau supérieur et exhortait le langage JavaScript à ne pas implémenter cette fonctionnalité. Quelques préoccupations spécifiques étaient :
- L'
await
de niveau supérieur pourrait bloquer l'exécution. - L'
await
de niveau supérieur pourrait bloquer la récupération de ressources. - Il n'y aurait aucune histoire d'interopérabilité claire pour les modules CommonJS.
La version au stade 3 de la proposition traite directement de ces problèmes :
- Comme les modules sœurs peuvent s'exécuter, il n'y a pas de blocage définitif.
- L'
await
de niveau supérieur se produit pendant la phase d'exécution du graphe des modules. À ce stade, toutes les ressources ont déjà été récupérées et liées. Il n'y a aucun risque de bloquer la récupération des ressources. - L'
await
de niveau supérieur est limité aux modules. Il n'y a explicitement aucun support pour les scripts ou pour les modules CommonJS.
Comme pour toute nouvelle fonctionnalité de langage, il y a toujours un risque de comportement inattendu. Par exemple, avec l'await
de niveau supérieur, les dépendances circulaires de modules pourraient introduire une impasse.
Sans l'await
de niveau supérieur, les développeurs JavaScript utilisaient souvent des expressions de fonction immédiatement invoquées asynchrones juste pour accéder à await
. Malheureusement, ce modèle entraîne moins de déterminisme dans l'exécution du graphe et l'analysabilité statique des applications. Pour ces raisons, l'absence d'await
de niveau supérieur était considérée comme un risque plus élevé que les dangers introduits par la fonctionnalité.