14 méthodes pour différer ou planifier JavaScript
Apprenez à différer et planifier JavaScript

Pourquoi différer ou planifier JavaScript ?
JavaScript peut (et va) ralentir votre site web de plusieurs façons. Cela peut avoir toutes sortes d'impacts négatifs sur les Core Web Vitals. JavaScript peut entrer en compétition pour les ressources réseau, il peut entrer en compétition pour les ressources CPU (bloquer le main thread) et il peut même bloquer l'analyse d'une page web. Différer et planifier vos scripts peut améliorer considérablement les Core Web Vitals.
Table of Contents!
- Pourquoi différer ou planifier JavaScript ?
- Comment le timing JavaScript peut-il affecter les Core Web Vitals ?
- Comment choisir la bonne méthode de report ?
- Méthode 1 : Utiliser l'attribut defer
- Méthode 2 : Utiliser l'attribut async
- Méthode 3 : Utiliser les modules
- Méthode 4 : Placer les scripts vers le bas de la page
- Méthode 5 : Injecter des scripts
- Méthode 6 : Injecter des scripts plus tard
- Méthode 7 : Changer le type de script (puis le rechanger)
- Méthode 8 : Utiliser l'intersection observer
- Méthode 9 : Utiliser readystatechange
- Méthode 10 : Utiliser setTimeout sans délai
- Méthode 11 : Utiliser setTimeout avec un délai
- Méthode 12 Utiliser une promesse pour définir une microtâche
- Méthode 13 Utiliser une microtâche
- Méthode 15 : Utiliser postTask
Pour minimiser les effets néfastes que JavaScript peut avoir sur les Core Web Vitals, c'est généralement une bonne idée de spécifier quand un script est mis en file d'attente pour le téléchargement et de planifier quand il peut prendre du temps CPU et bloquer le main thread.
Comment le timing JavaScript peut-il affecter les Core Web Vitals ?
Comment le timing JavaScript peut-il affecter les Core Web Vitals ? Jetez un œil à cet exemple réel. La première page est chargée avec du JavaScript 'render blocking'. Les métriques de peinture ainsi que le Time to interactive sont assez mauvais. Le deuxième exemple est exactement la même page mais avec le JavaScript différé. Vous verrez que l'image LCP a quand même pris un gros coup. Le troisième exemple a le même script exécuté après le 'load event' de la page et a les appels de fonction décomposés en morceaux plus petits. Ce dernier passe les Core Web Vitals avec de la marge.



Par défaut, le JavaScript externe dans le head de la page bloquera la création de l'arbre de rendu. Plus spécifiquement : lorsque le navigateur rencontre un script dans le document, il doit mettre en pause la construction du DOM, passer le contrôle au runtime JavaScript, et laisser le script s'exécuter avant de poursuivre la construction du DOM. Cela affectera vos Paint Metrics (Largest Contentful Paint et First Contentful Paint).
Le JavaScript différé ou async peut toujours impacter les métriques de peinture, en particulier le Largest Contentful Paint car il s'exécutera, et bloquera le main thread, une fois que le DOM a été créé (et les éléments LCP courants comme les images pourraient ne pas avoir été téléchargés).
Les fichiers JavaScript externes vont aussi entrer en compétition pour les ressources réseau. Les fichiers JavaScript externes sont généralement téléchargés plus tôt que les images. SI vous téléchargez trop de scripts, le téléchargement de vos images sera retardé.
Enfin et surtout, JavaScript peut bloquer ou retarder l'interaction utilisateur. Lorsqu'un script utilise des ressources CPU (bloquant le main thread), un navigateur ne répondra pas aux entrées (clics, défilement, etc.) tant que ce script n'est pas terminé.
Comment la planification ou le fait de différer JavaScript corrige-t-il les Core Web Vitals ?
Comment choisir la bonne méthode de report ?
Tous les scripts ne sont pas identiques et chaque script a sa propre fonctionnalité. Certains scripts sont importants à avoir tôt dans le processus de rendu, d'autres non.

J'aime catégoriser les JavaScripts en 4 groupes basés sur leur niveau d'importance.
1. Critique pour le rendu. Ce sont les scripts qui changeront l'apparence d'une page web. S'ils ne se chargent pas, la page ne semblera pas complète. Ces scripts doivent être évités à tout prix. Si vous ne pouvez pas les éviter pour une raison quelconque, ils ne doivent pas être différés. Par exemple un slider supérieur ou un script de test A/B.
2. Critique. Ces scripts ne changeront pas l'apparence d'une page web (trop) mais la page ne fonctionnera pas bien sans eux. Ces scripts doivent être différés ou mis en async. Par exemple vos scripts de menu.
3. Important. Ce sont des scripts que vous voulez charger car ils sont précieux pour vous ou le visiteur. J'ai tendance à charger ces scripts après que le load event a été déclenché. Par exemple analytics ou un bouton 'retour en haut'.
4. Nice to have. Ce sont des scripts dont vous pouvez vous passer si vous en avez absolument besoin. Je charge ces scripts avec la priorité la plus basse et je les exécute seulement quand le navigateur est inactif (idle). Par exemple un widget de chat ou un bouton facebook.
Méthode 1 : Utiliser l'attribut defer
Les scripts avec l'attribut defer se téléchargeront en parallèle et sont ajoutés à la file d'attente JavaScript différée. Juste avant que le navigateur ne déclenche l'événement DOMContentLoaded, tous les scripts de cette file d'attente s'exécuteront dans l'ordre dans lequel ils apparaissent dans le document.
<script src='javascript.js'></script> Le 'truc defer' corrige généralement beaucoup de problèmes, surtout les métriques de peinture. Malheureusement il n'y a pas de garantie, cela dépend de la qualité des scripts. Les scripts différés s'exécuteront une fois que tous les scripts ont été chargés et que le HTML est analysé (DOMContentLoaded). L'élément LCP (généralement une grande image) pourrait ne pas être chargé d'ici là et les scripts différés causeront toujours un retard dans le LCP.
Quand l'utiliser :
Utilisez des scripts différés pour les scripts Critiques qui sont nécessaires dès que possible.
Avantages :
- Les scripts différés se téléchargeront en parallèle
- Le DOM sera disponible au moment de l'exécution
Désavantages :
- Les scripts différés pourraient retarder vos métriques LCP
- Les scripts différés bloqueront le main thread une fois exécutés
- Il pourrait ne pas être sûr de différer des scripts lorsque des scripts inline ou async dépendent d'eux
Méthode 2 : Utiliser l'attribut async
Les scripts avec l'attribut async se téléchargent en parallèle et s'exécuteront immédiatement après avoir fini de se télécharger.
<script src='javascript.js'></script> Les scripts async feront peu pour corriger vos problèmes de vitesse de page. C'est génial qu'ils soient téléchargés en parallèle mais c'est à peu près tout. Une fois téléchargés, ils bloqueront le main thread lorsqu'ils seront exécutés.
Quand l'utiliser :
Utilisez des scripts async pour les scripts Critiques qui sont nécessaires dès que possible et sont autonomes (ne dépendent pas d'autres scripts).
Avantages :
- Les scripts async se téléchargeront en parallèle.
- Les scripts async s'exécuteront dès que possible.
Désavantages :
- DOMContentLoaded peut se produire avant et après async.
- L'ordre d'exécution des scripts sera inconnu à l'avance.
- Vous ne pouvez pas utiliser de scripts async qui dépendent d'autres scripts async ou différés
Méthode 3 : Utiliser les modules
Les scripts modulaires sont différés par défaut à moins qu'ils n'aient l'attribut async. Dans ce cas, ils seront traités comme des scripts async
<script src='javascript.js'></script> Les modules sont une nouvelle façon de penser JavaScript et corrigent certains défauts de conception. À part cela, l'utilisation de modules de script n'accélérera pas votre site web.
Quand l'utiliser :
Lorsque votre application est construite de manière modulaire, il est logique d'utiliser également des modules JavaScript.
Avantages :
- Les modules sont différés par défaut
- Les modules sont plus faciles à maintenir et fonctionnent très bien avec la conception web modulaire
- Les modules permettent un découpage de code facile avec des imports dynamiques où vous n'importez que les modules dont vous avez besoin à un certain moment.
Désavantages :
- Les modules eux-mêmes n'amélioreront pas les Core Web Vitals
- Importer des modules just-in-time ou à la volée pourrait être lent et aggraver le FID et l'INP
Méthode 4 : Placer les scripts vers le bas de la page
Les scripts de pied de page sont mis en file d'attente pour le téléchargement plus tard. Cela priorisera d'autres ressources qui sont dans le document au-dessus de la balise script.
<html>
<head></head>
<body>
[votre contenu de page ici]
<script defer src='javascript.js'></script>
</body>
</html> Placer vos scripts en bas de la page est une technique intéressante. Cela planifiera d'autres ressources (comme les images) avant vos scripts. Cela augmentera la chance qu'elles soient disponibles pour le navigateur et peintes à l'écran avant que les fichiers JavaScript n'aient fini de se télécharger et que le main thread ne soit bloqué par l'exécution du script. Pourtant... aucune garantie.
Quand l'utiliser :
Lorsque vos scripts fonctionnent déjà assez bien mais que vous voulez prioriser légèrement d'autres ressources comme les images et les polices web.
Avantages :
- Placer les scripts en bas de la page ne nécessite pas beaucoup de connaissances.
- Si c'est fait correctement, il n'y a pas de risque que cela casse votre page
Désavantages :
- Les scripts critiques pourraient être téléchargés et exécutés plus tard
- Cela ne corrige aucun problème JavaScript sous-jacent
Méthode 5 : Injecter des scripts
Les scripts injectés sont traités comme des scripts async. Ils sont téléchargés en parallèle et sont exécutés immédiatement après le téléchargement.
<script>
const loadScript = (scriptSource) => {
const script = document.createElement('script');
script.src = scriptSource;
document.head.appendChild(script);
}
// appeler la fonction loadscript qui injecte 'javascript.js'
loadScript('javascript.js');
</script> D'un point de vue Core Web Vitals, cette technique est exactement la même que l'utilisation de <script async>.
Quand l'utiliser :
Cette méthode est souvent utilisée par des scripts tiers qui se déclenchent le plus tôt possible. L'appel de fonction rend facile l'encapsulation et la compression du code.
Avantages :
- Code contenu qui injecte un script async.
Désavantages :
- DOMContentLoaded peut se produire à la fois avant et après que le script soit chargé.
- L'ordre d'exécution du script sera inconnu à l'avance.
- Vous ne pouvez pas utiliser cela sur des scripts qui dépendent d'autres scripts async ou différés
Méthode 6 : Injecter des scripts plus tard
Les scripts Nice-to-have ne devraient à mon avis jamais être chargés en différé. Ils devraient être injectés au moment le plus opportun. Dans l'exemple ci-dessous, le script s'exécutera après que le navigateur a envoyé le 'load event'.
<script>
window.addEventListener('load', function () {
// voir méthode 5 pour la fonction loadscript
loadScript('javascript.js');
});
</script> C'est la première technique qui améliorera de manière fiable le Largest Contentful paint. Toutes les ressources importantes, y compris les images, seront téléchargées lorsque le navigateur déclenche le 'load event'. Cela pourrait introduire toutes sortes de problèmes car cela pourrait prendre beaucoup de temps pour que le load event soit appelé.
Quand l'utiliser :
Pour les scripts nice-to-have qui n'ont aucune raison d'impacter les métriques de peinture.
Avantages :
- N'entrera pas en compétition pour les ressources critiques car il injectera le script une fois que la page et ses ressources ont chargé
Désavantages :
- Si votre page est mal conçue du point de vue Core Web Vitals, cela pourrait prendre beaucoup de temps pour que la page envoie le 'load' event
- Vous devez faire attention à ne pas appliquer cela aux scripts critiques (comme le lazy loading, menu, etc.)
Méthode 7 : Changer le type de script (puis le rechanger)
Si une balise script est trouvée quelque part sur la page qui 1. a un attribut type et 2. l'attribut type n'est pas "text/javascript", le script ne sera pas téléchargé et exécuté par un navigateur. De nombreux chargeurs JavaScript (comme RocketLoader de CloudFlare) reposent sur ce principe. L'idée est assez simple et élégante.
D'abord tous les scripts sont réécrits comme ceci :
<script src="javascript.js"></script> Ensuite, à un moment donné pendant le processus de chargement, ces scripts sont reconvertis en 'javascripts normaux'.
Quand l'utiliser :
Ce n'est pas une méthode que je recommanderais. Corriger l'impact JavaScript prendra beaucoup plus que simplement déplacer chaque script un peu plus loin dans la file d'attente. D'un autre côté, si vous avez peu de contrôle sur les scripts s'exécutant sur la page ou avez des connaissances JavaScript insuffisantes, cela pourrait être votre meilleur pari.
Avantages :
- C'est facile, activez juste rocket loader ou un autre plugin et tous vos scripts sont maintenant exécutés à un moment un peu plus tardif.
- CELA corrigera probablement vos métriques de peinture à condition que vous n'ayez pas utilisé de lazy loading basé sur JS.
- Cela fonctionnera pour les scripts inline et externes.
Désavantages :
- Vous n'aurez aucun contrôle fin sur le moment où les scripts s'exécutent
- Un script mal écrit pourrait casser
- Cela utilise JavaScript pour corriger JavaScript
- Cela ne fait rien pour corriger les scripts à longue exécution
Méthode 8 : Utiliser l'intersection observer
Avec l'intersection observer, vous pouvez exécuter une fonction (qui dans ce cas charge un JavaScript externe) lorsqu'un élément défile dans la fenêtre visible (viewport).
<script>
const handleIntersection = (entries, observer) => {
if (entries?.[0].isIntersecting) {
// charger votre script ou exécuter une autre
fonction comme déclencher un élément chargé paresseusement (lazy loaded)
loadScript('javascript.js');
// retirer l'observateur
observer.unobserve(entries?.[0].target);
}
};
const Observer = new window.IntersectionObserver()
Observer.observe(document.querySelector('footer'));
</script> C'est de loin la méthode la plus efficace pour différer JavaScript qui existe. Chargez seulement les scripts dont vous avez besoin, juste avant d'en avoir besoin. Malheureusement, la vie réelle est rarement aussi propre et peu de scripts peuvent être liés à un élément qui défile dans la vue.
Quand l'utiliser :
Utilisez cette technique autant que possible ! Chaque fois qu'un script interagit seulement avec un composant hors écran (comme une carte, un slider, un formulaire), c'est la meilleure façon d'injecter ce script.
Avantages :
- N'interférera pas avec les Core Web Vitals LCP et FCP
- N'injectera jamais de scripts qui ne sont pas utilisés. Cela améliorera le FID et l'INP
Désavantages :
- Ne doit pas être utilisé avec des composants qui pourraient être dans le viewport visible
- Est plus difficile à maintenir que le basique <script src="...">
- Pourrait introduire un layout shift
Méthode 9 : Utiliser readystatechange
document.readystate peut être utilisé comme une alternative à l'événement 'DOMContentloaded' et 'load'. Le readystate 'interactive' est généralement un bon endroit pour appeler des scripts critiques qui doivent changer le DOM ou ajouter des gestionnaires d'événements.
Le readystate 'complete' est un bon endroit pour appeler des scripts qui sont moins critiques.
document.addEventListener('readystatechange', (event) => {
if (event.target.readyState === 'interactive') {
initLoader();
} else if (event.target.readyState === 'complete') {
initApp();
}
}); Méthode 10 : Utiliser setTimeout sans délai
setTimeout est une méthode mal vue mais fortement sous-estimée dans la communauté pagespeed. setTimeout a mauvaise réputation car il est souvent mal utilisé. De nombreux développeurs croient que setTimeout ne peut être utilisé que pour retarder l'exécution du script du nombre de millisecondes défini. Bien que ce soit vrai, setTimeout fait en fait quelque chose de bien plus intéressant. Il crée une nouvelle tâche à la fin de la boucle d'événements du navigateur. Ce comportement peut être utilisé pour planifier vos tâches efficacement. Il peut également être utilisé pour décomposer de longues tâches en tâches plus petites séparées
<script>
setTimeout(() => {
// charger un script ou exécuter une autre fonction
console.log('- Je suis appelé depuis un timeOut() de 0ms')
}, 0);
console.log('- J'étais dernier dans la file mais exécuté en premier')
/*
Sortie :
- J'étais dernier dans la file mais exécuté en premier
- Je suis appelé depuis un timeOut() de 0ms
*/
</script> Quand l'utiliser :
setTimeout a créé une nouvelle tâche dans la boucle d'événements du navigateur. Utilisez ceci lorsque votre main thread est bloqué par de nombreux appels de fonction qui s'exécutent séquentiellement.
Avantages :
- Peut décomposer du code à longue exécution en morceaux plus petits.
Désavantages :
- setTimeout est une méthode plutôt rudimentaire et n'offre pas de priorisation pour les scripts importants.
- Ajoutera le code à exécuter à la fin de la boucle
Méthode 11 : Utiliser setTimeout avec un délai
Les choses deviennent encore plus intéressantes lorsque nous appelons setTimeout avec un délai de plus de 0ms
<script>
setTimeout(() => {
// charger un script ou exécuter une autre fonction
console.log('- Je suis appelé depuis un timeOut() de 10ms')
}, 10);
setTimeout(() => {
// charger un script ou exécuter une autre fonction
console.log('- Je suis appelé depuis un timeOut() de 0ms')
}, 0);
console.log('- J'étais dernier dans la file mais exécuté en premier')
/*
Sortie :
- J'étais dernier dans la file mais exécuté en premier
- Je suis appelé depuis un timeOut() de 0ms
- Je suis appelé depuis un timeOut() de 10ms
*/
</script> Quand l'utiliser :
Lorsque vous avez besoin d'une méthode facile pour planifier un script après un autre, un petit délai fera l'affaire
Avantages :
- Supporté sur tous les navigateurs
Désavantages :
- N'offre pas de planification avancée
Méthode 12 Utiliser une promesse pour définir une microtâche
Créer une micro-tâche est aussi une façon intéressante de planifier JavaScript. Les micro-tâches sont planifiées pour une exécution immédiatement après que la boucle d'exécution actuelle a fini.
<script>
const myPromise = new Promise((resolve, reject) => {
resolve();
}).then(
() => {
console.log('- J'ai été planifié après une promesse')
}
);
console.log('- J'étais dernier dans la file mais exécuté en premier')
/*
Sortie :
- J'étais dernier dans la file mais exécuté en premier
- J'ai été planifié après une promesse
*/
</script> Quand l'utiliser :
Lorsqu'une tâche doit être planifiée immédiatement après une autre tâche.
Avantages :
- La microtâche sera planifiée immédiatement après que la tâche a fini de s'exécuter.
- Une microtâche peut être utilisée pour retarder des morceaux de code JavaScript moins importants dans le même événement.
Désavantages :
- Ne décomposera pas le main thread en plus petites parties. Le navigateur n'aura aucune chance de répondre à l'entrée utilisateur.
- Vous n'aurez probablement jamais besoin d'utiliser des microtâches pour améliorer les Core Web Vitals à moins que vous ne sachiez déjà exactement ce que vous faites.
Méthode 13 Utiliser une microtâche
Le même résultat peut être obtenu en utilisant queueMicrotask(). L'avantage d'utiliser queueMicrotask() par rapport à une promesse est qu'il est légèrement plus rapide et vous n'avez pas besoin de gérer vos promesses.
<script>
queueMicrotask(() => {
console.log('- Je suis une microtâche')
})
console.log('- J'étais dernier dans la file mais exécuté en premier')
/*
Sortie :
- J'étais dernier dans la file mais exécuté en premier
- Je suis une microtâche
*/
</script> Méthode 14 : Utiliser requestIdleCallback
La méthode window.requestIdleCallback() met en file d'attente une fonction à appeler pendant les périodes d'inactivité (idle) d'un navigateur. Cela permet aux développeurs d'effectuer un travail de fond et de faible priorité sur la boucle d'événements principale, sans impacter les événements critiques pour la latence tels que l'animation et la réponse aux entrées. Les fonctions sont généralement appelées dans l'ordre premier entré, premier sorti ; cependant, les rappels (callbacks) qui ont un timeout spécifié peuvent être appelés dans le désordre si nécessaire afin de les exécuter avant que le timeout ne s'écoule.
<script>
requestIdleCallback(() => {
const script = document.createElement('script');
script.src = 'javascript.js';
document.head.appendChild(script);
});
</script> Quand l'utiliser :
Utilisez ceci pour les scripts qui sont Nice to have ou pour gérer des tâches non critiques après l'entrée utilisateur
Avantages :
- Exécuter JavaScript avec un impact minimal pour l'utilisateur
- Améliorera très probablement le FID et l'INP
Désavantages :
- Supporté dans la plupart des navigateurs mais pas tous. Il existe des poly-fills qui se rabattent sur setTimeout();
- Aucune garantie que le code se déclenchera un jour
Méthode 15 : Utiliser postTask
La méthode permet aux utilisateurs de spécifier optionnellement un délai minimum avant que la tâche ne s'exécute, une priorité pour la tâche, et un signal qui peut être utilisé pour modifier la priorité de la tâche et/ou annuler la tâche. Elle renvoie une promesse qui est résolue avec le résultat de la fonction de rappel de la tâche, ou rejetée avec la raison de l'annulation ou une erreur levée dans la tâche.
<script>
scheduler.postTask(() => {
const script = document.createElement('script');
script.src = 'javascript.js';
document.head.appendChild(script);
}, { priority: 'background' });
</script> Quand l'utiliser :
postTask est l'API parfaite pour planifier un script. Malheureusement, le support du navigateur à ce moment est mauvais donc vous ne devriez pas l'utiliser.
Avantages :
- Contrôle complet sur la planification de l'exécution JavaScript !
Désavantages :
- Non supporté dans certains navigateurs importants.
Make decisions with Data.
You cannot optimize what you do not measure. Install the CoreDash pixel and capture 100% of user experiences.
- 100% Capture
- Data Driven
- Easy Install

