Optimiser l'exécution JavaScript

JavaScript déclenche souvent des modifications visuelles. Parfois, c'est directement par le biais de manipulations de style, et parfois il s'agit de calculs qui entraînent des changements visuels, comme la recherche ou le tri de données. Les problèmes de performances peuvent souvent être dus à des codes JavaScript dont le délai d'exécution est incorrect ou de longue durée. Vous devez chercher à en minimiser l'impact lorsque vous le pouvez.

Paul Lewis
Paul Lewis

JavaScript déclenche souvent des modifications visuelles. Parfois, cela se fait directement grâce à des manipulations de style, et parfois ce sont des calculs qui entraînent des modifications visuelles, comme la recherche ou le tri de données. Les problèmes de performances peuvent souvent être dus à des codes JavaScript dont le délai d'exécution est incorrect ou de longue durée. Vous devez chercher à en minimiser l'impact lorsque vous le pouvez.

Le profilage des performances JavaScript peut s'avérer être un art, car le JavaScript que vous écrivez ne ressemble pas au code qui est réellement exécuté. Les navigateurs modernes utilisent des compilateurs JIT et toutes sortes d'optimisations et d'astuces pour vous offrir une exécution la plus rapide possible, ce qui modifie considérablement la dynamique du code.

Toutefois, vous pouvez tout à fait faire certaines choses pour aider vos applications à bien exécuter JavaScript.

Résumé

  • Évitez setTimeout ou setInterval pour les mises à jour visuelles. Utilisez toujours requestAnimationFrame à la place.
  • Déplacement du code JavaScript de longue durée du thread principal vers les workers Web.
  • Effectuez des microtâches pour apporter des modifications DOM sur plusieurs images.
  • Utilisez la timeline des outils pour les développeurs Chrome et le Profileur JavaScript pour évaluer l'impact de JavaScript.

Utiliser requestAnimationFrame pour les modifications visuelles

Lorsque des modifications visuelles se produisent à l'écran, vous devez effectuer votre travail au bon moment pour le navigateur, c'est-à-dire au début du frame. Le seul moyen de garantir que votre script JavaScript s'exécutera au début d'un frame consiste à utiliser requestAnimationFrame.

/**
    * If run as a requestAnimationFrame callback, this
    * will be run at the start of the frame.
    */
function updateScreen(time) {
    // Make visual updates here.
}

requestAnimationFrame(updateScreen);

Les frameworks ou exemples peuvent utiliser setTimeout ou setInterval pour effectuer des modifications visuelles telles que des animations, mais le problème est que le rappel s'exécutera à un certain point du frame, éventuellement juste à la fin, ce qui peut souvent nous faire manquer une image, ce qui entraînera des à-coups.

La fonction setTimeout oblige le navigateur à manquer une image.

Auparavant, jQuery utilisait auparavant setTimeout pour son comportement animate. Elle a été modifiée pour utiliser requestAnimationFrame dans la version 3. Si vous utilisez une ancienne version de jQuery, vous pouvez la corriger pour utiliser requestAnimationFrame, ce qui est vivement recommandé.

Réduisez la complexité ou utilisez des Web workers

JavaScript s'exécute sur le thread principal du navigateur, à côté des calculs de style, de la mise en page et, dans de nombreux cas, de l'effet Paint. Si votre code JavaScript s'exécute pendant une longue période, il bloque ces autres tâches, ce qui peut entraîner la perte de certaines images.

Vous devez définir de façon tactique le moment et la durée d'exécution de JavaScript. Par exemple, dans une animation telle que le défilement, vous devriez idéalement chercher à limiter le code JavaScript à une région de 3-4 ms. Au-delà, vous risquez de prendre trop de temps. Pendant une période d'inactivité, vous pouvez vous permettre d'être plus détendu(e) quant au temps nécessaire.

Dans de nombreux cas, vous pouvez déplacer des tâches de calcul pur vers des nœuds de calcul Web si, par exemple, ils ne nécessitent pas d'accès DOM. La manipulation ou le balayage de données, comme le tri ou la recherche, conviennent souvent à ce modèle, tout comme le chargement et la génération de modèles.

var dataSortWorker = new Worker("sort-worker.js");
dataSortWorker.postMesssage(dataToSort);

// The main thread is now free to continue working on other things...

dataSortWorker.addEventListener('message', function(evt) {
    var sortedData = evt.data;
    // Update data on screen...
});

Certaines tâches ne correspondent pas à ce modèle: les Web workers ne disposent pas d'un accès DOM. Lorsque votre travail doit se trouver sur le thread principal, envisagez une approche par lot, dans laquelle vous segmentez la tâche plus importante en microtâches, chacune ne prenant pas plus de quelques millisecondes, et exécutez-la dans des gestionnaires requestAnimationFrame sur chaque image.

Cette approche a des conséquences sur l'expérience utilisateur et l'interface utilisateur. Vous devez vous assurer que l'utilisateur sait qu'une tâche est en cours de traitement, en utilisant un indicateur de progression ou d'activité. Dans tous les cas, cette approche laissera le thread principal de votre application libre, ce qui l'aidera à rester réactif aux interactions utilisateur.

Connaître la "taxe sur les cadres" de votre JavaScript

Lorsque vous évaluez un framework, une bibliothèque ou votre propre code, il est important d'évaluer le coût d'exécution du code JavaScript frame par frame. Cela est particulièrement important lorsque vous effectuez des tâches d'animation critiques comme la transition ou le défilement.

Le panneau "Performances" des outils pour les développeurs Chrome est le meilleur moyen de mesurer le coût de votre code JavaScript. En général, vous obtenez des enregistrements de bas niveau comme suit:

Enregistrement des performances dans les outils pour les développeurs Chrome

La section Main fournit un graphique de type "flamme" des appels JavaScript, ce qui vous permet d'analyser exactement quelles fonctions ont été appelées et au bout de combien de temps chacune a été appelée.

Grâce à ces informations, vous pouvez évaluer l'impact du code JavaScript en termes de performances sur votre application, et commencer à détecter et à corriger les hotspots où l'exécution des fonctions prend trop de temps. Comme indiqué précédemment, vous devez chercher à supprimer le code JavaScript de longue durée ou, si ce n'est pas possible, le déplacer vers un nœud de calcul Web afin de libérer le thread principal pour effectuer d'autres tâches.

Consultez la section Premiers pas avec l'analyse des performances d'exécution pour découvrir comment utiliser le panneau "Performances".

Éviter la micro-optimisation de votre code JavaScript

Il peut être intéressant de savoir que le navigateur peut exécuter une version d'un élément 100 fois plus vite qu'un autre. Par exemple, demander la propriété offsetTop d'un élément est plus rapide que de calculer getBoundingClientRect(). Cependant, il est presque toujours vrai que vous n'appellez des fonctions de ce type qu'un petit nombre de fois par frame. Il est donc généralement inutile de se concentrer sur cet aspect des performances de JavaScript. En général, vous ne gagnez que quelques fractions de millisecondes.

Si vous créez un jeu ou une application gourmande en ressources de calcul, vous faites probablement exception à cette consigne, car vous intégrerez généralement un grand nombre de calculs dans un seul frame. Dans ce cas, tout vous aide.

En bref, vous devez vous méfier des micro-optimisations, car elles ne correspondent généralement pas au type d'application que vous créez.