Dans les sites Web modernes, les scripts sont souvent âplus lourdsâ que le HTML: leur taille de téléchargement est plus grande et le temps de traitement est également plus long.
Lorsque le navigateur charge le HTML et rencontre une balise <script>...</script>, il ne peut pas continuer à construire le DOM. Il doit exécuter le script de suite. Il en va de même pour les scripts externes <script src ="..."></script>: le navigateur doit attendre le téléchargement du script, lâexécuter, puis traiter le reste de la page.
Cela conduit à deux problèmes importants:
- Les scripts ne peuvent pas voir les éléments DOM en dessous dâeux, ils ne peuvent donc pas ajouter de gestionnaires, etc.
- Sâil y a un script volumineux en haut de la page, il âbloque la pageâ. Les utilisateurs ne peuvent pas voir le contenu de la page tant quâil nâest pas téléchargé et exécuté:
<p>...content before script...</p>
<script src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>
<!-- Ceci n'est pas visible tant que le script n'est pas chargé -->
<p>...content after script...</p>
Il existe quelques solutions pour contourner cela. Par exemple, nous pouvons mettre un script en bas de page. Comme ça, il peut voir les éléments au-dessus, et cela ne bloque pas lâaffichage du contenu de la page:
<body>
...all content is above the script...
<script src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>
</body>
Mais cette solution est loin dâêtre parfaite. Par exemple, le navigateur remarque le script (et peut commencer à le télécharger) uniquement après avoir téléchargé le document HTML complet. Pour les longs documents HTML, cela peut être un retard notable.
De telles choses sont invisibles pour les personnes utilisant des connexions très rapides, mais de nombreuses personnes dans le monde ont encore des vitesses Internet lentes et utilisent une connexion Internet mobile loin dâêtre parfaite.
Heureusement, il y a deux attributs de <script> qui résolvent le problème pour nous: defer et async.
defer
Lâattribut defer indique au navigateur de ne pas attendre le script. Au lieu de cela, le navigateur continuera à traiter le HTML, à construire le DOM. Le script se charge âen arrière-planâ, puis sâexécute lorsque le DOM est entièrement construit.
Voici le même exemple que ci-dessus, mais avec defer :
<p>...content before script...</p>
<script defer src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>
<!-- visible immédiatement -->
<p>...content after script...</p>
- Les scripts avec
deferne bloquent jamais la page. - Les scripts avec
defersâexécutent toujours lorsque le DOM est prêt, mais avant lâévénementDOMContentLoaded.
Lâexemple suivant montre que:
<p>...content before scripts...</p>
<script>
document.addEventListener('DOMContentLoaded', () => alert("DOM ready after defer!")); // (2)
</script>
<script defer src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>
<p>...content after scripts...</p>
- Le contenu de la page sâaffiche immédiatement.
DOMContentLoadedattend le script différé. Il ne se déclenche que lorsque le script(2)est téléchargé et exécuté.
Les scripts différés conservent leur ordre relatif, tout comme les scripts classiques.
Donc, si nous avons dâabord un long script, puis un plus petit, alors ce dernier attend.
<script defer src="https://javascript.info/article/script-async-defer/long.js"></script>
<script defer src="https://javascript.info/article/script-async-defer/small.js"></script>
Les navigateurs analysent la page à la recherche de scripts et les téléchargent en parallèle pour améliorer les performances. Ainsi, dans lâexemple ci-dessus, les deux scripts se téléchargent en parallèle. Le small.js se termine probablement en premier.
⦠Mais lâattribut defer, en plus de dire au navigateur âde ne pas bloquerâ, garantit que lâordre relatif est conservé. Ainsi, même si small.js se charge en premier, il attend et sâexécute toujours après lâexécution de long.js.
Mais la spécification exige que les scripts sâexécutent dans lâordre des documents, donc elle attend que long.js sâexécute.
```smart header="L'attribut `defer` est uniquement pour les scripts externes"
L'attribut `defer` est ignoré si la balise `<script>` n'a pas de `src`.
async
- Le navigateur ne bloque pas les scripts
async(commedefer). - Dâautres scripts nâattendent pas les scripts
async, et les scriptsasyncne les attendent pas. DOMContentLoadedet les scripts asynchrones ne sâattendent pas :DOMContentLoadedpeut se produire à la fois avant un script asynchrone (si un script async termine le chargement une fois la page terminée)- ⦠ou après un script async (si un script async est court ou était dans le cache HTTP)
En dâautres termes, les scripts async se chargent en arrière-plan et sâexécutent lorsquâils sont prêts. Le DOM et les autres scripts ne les attendent pas, et ils nâattendent rien. Un script entièrement indépendant qui sâexécute lorsquâil est chargé. Aussi simple que cela puisse être, non ?
Donc, si nous avons plusieurs scripts async, ils peuvent sâexécuter dans nâimporte quel ordre. Premier chargé â premier exécuté:
<p>...content before scripts...</p>
<script>
document.addEventListener('DOMContentLoaded', () => alert("DOM ready!"));
</script>
<script async src="https://javascript.info/article/script-async-defer/long.js"></script>
<script async src="https://javascript.info/article/script-async-defer/small.js"></script>
<p>...content after scripts...</p>
- Le contenu de la page apparaît immédiatement:
asyncne la bloque pas. DOMContentLoadedpeut arriver soit avant ou aprèsasync, aucune garantie ici.- Les scripts asynchrones nâattendent pas les uns les autres. Un script plus petit
small.jspasse en second, mais se charge probablement avantlong.js, donc sâexécute en premier. Câest ce quâon appelle une commande âload-firstâ.
Les scripts asynchrones sont parfaits lorsque nous intégrons un script tiers indépendant dans la page: compteurs, publicités, etc., car ils ne dépendent pas de nos scripts et nos scripts ne doivent pas les attendre:
<!-- Google Analytics est généralement ajouté comme ceci -->
<script async src="https://google-analytics.com/analytics.js"></script>
async est uniquement pour les scripts externesTout comme defer, lâattribut async est ignoré si la balise <script> nâa pas de src.
Les scripts dynamiques
Il existe un autre moyen important dâajouter un script à la page.
Nous pouvons également ajouter un script dynamiquement en utilisant JavaScript:
let script = document.createElement('script');
script.src = "/article/script-async-defer/long.js";
document.body.append(script); // (*)
Le script commence à se charger dès quâil est ajouté au document (*).
Les scripts dynamiques se comportent comme âasynchronesâ par défaut.
Câest-Ã -dire :
- Ils nâattendent rien, rien ne les attend.
- Le script qui se charge en premier â sâexécute en premier (âload-firstâ).
let script = document.createElement('script');
script.src = "/article/script-async-defer/long.js";
script.async = false;
document.body.append(script);
Par exemple, nous ajoutons ici deux scripts. Sans script.async=false, ils sâexécuteraient dans lâordre de chargement (le small.js probablement en premier). Mais avec, lâordre est âcomme dans le documentâ:
function loadScript(src) {
let script = document.createElement('script');
script.src = src;
script.async = false;
document.body.append(script);
}
// long.js s'exécute en premier à cause de async=false
loadScript("/article/script-async-defer/long.js");
loadScript("/article/script-async-defer/small.js");
Résumé
Async et defer ont un point commun: le téléchargement de tels scripts ne bloque pas le rendu des pages. Ainsi, lâutilisateur peut lire le contenu de la page et se familiariser immédiatement avec la page.
Mais il existe également des différences essentielles entre eux:
En pratique, defer est utilisé pour les scripts qui ont besoin de tout le DOM et/ou leur ordre dâexécution relatif est important.
Et async est utilisé pour des scripts indépendants, comme des compteurs ou des publicités. Et leur ordre dâexécution relatif nâa pas dâimportance.
Veuillez noter que si vous utilisez defer ou async, lâutilisateur verra alors la page avant le chargement du script.
Lâutilisateur peut donc lire la page, mais certains composants graphiques ne sont probablement pas encore prêts.
Il devrait y avoir des indications de âchargementâ aux bons endroits et les boutons désactivés devraient sâafficher comme tels, afin que lâutilisateur puisse voir clairement ce qui est prêt et ce qui ne lâest pas.

Commentaires
<code>, pour plusieurs lignes â enveloppez-les avec la balise<pre>, pour plus de 10 lignes - utilisez une sandbox (plnkr, jsbin, codepenâ¦)