🌐 AIæœçŽą & 代理 䞻饔
11 juillet 2023

Les scripts: async, defer

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:

  1. Les scripts ne peuvent pas voir les Ă©lĂ©ments DOM en dessous d’eux, ils ne peuvent donc pas ajouter de gestionnaires, etc.
  2. 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 defer ne bloquent jamais la page.
  • Les scripts avec defer s’exĂ©cutent toujours lorsque le DOM est prĂȘt, mais avant l’évĂ©nement DOMContentLoaded.

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>
  1. Le contenu de la page s’affiche immĂ©diatement.
  2. DOMContentLoaded attend 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 (comme defer).
  • D’autres scripts n’attendent pas les scripts async, et les scripts async ne les attendent pas.
  • DOMContentLoaded et les scripts asynchrones ne s’attendent pas :
    • DOMContentLoaded peut 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: async ne la bloque pas.
  • DOMContentLoaded peut arriver soit avant ou aprĂšs async, aucune garantie ici.
  • Les scripts asynchrones n’attendent pas les uns les autres. Un script plus petit small.js passe en second, mais se charge probablement avant long.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>
L’attribut async est uniquement pour les scripts externes

Tout 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:

L’ordre DOMContentLoaded
async Load-first. Leur ordre dans le document n’a pas d’importance – premier chargĂ©, premier exĂ©cutĂ© Sans importance. Peut se charger et s’exĂ©cuter alors que le document n’a pas encore Ă©tĂ© entiĂšrement tĂ©lĂ©chargĂ©. Cela se produit si les scripts sont petits ou mis en cache et que le document est suffisamment long.
defer Ordre de document (tel qu’il apparaĂźt dans le document). ExĂ©cute aprĂšs le chargement et l’analyse du document (ils attendent si nĂ©cessaire), juste avant DOMContentLoaded.

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.

La page sans scripts devrait ĂȘtre utilisable

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.

Carte du tutoriel

Commentaires

lire ceci avant de commenter

  • Si vous avez des amĂ©liorations Ă  suggĂ©rer, merci de soumettre une issue GitHub ou une pull request au lieu de commenter.
  • Si vous ne comprenez pas quelque chose dans l'article, merci de prĂ©ciser.
  • Pour insĂ©rer quelques bouts de code, utilisez la balise <code>, pour plusieurs lignes – enveloppez-les avec la balise <pre>, pour plus de 10 lignes - utilisez une sandbox (plnkr, jsbin, codepen
)