Optimiser l'exécution JavaScript

JavaScript déclenche souvent des modifications visuelles. Parfois, cela se fait directement via 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 scripts JavaScript mal synchronisés ou de longue durée sont une cause courante de problèmes de performances. Vous devez essayer de limiter son impact dans la mesure du possible.

JavaScript déclenche souvent des modifications visuelles. Parfois, cela se fait directement via 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 scripts JavaScript mal synchronisés ou de longue durée sont une cause courante de problèmes de performances. Vous devez essayer de limiter son impact dans la mesure du possible.

Le profilage des performances JavaScript peut s'apparenter à un art, car le code JavaScript que vous écrivez ne ressemble en rien au code qui est réellement exécuté. Les navigateurs modernes utilisent des compilateurs JIT et toutes sortes d'optimisations et d'astuces pour essayer de vous offrir l'exécution la plus rapide possible. Cela modifie considérablement la dynamique du code.

Toutefois, vous pouvez prendre certaines mesures pour aider vos applications à exécuter JavaScript correctement.

Résumé

  • Évitez d'utiliser setTimeout ou setInterval pour les mises à jour visuelles. Utilisez toujours requestAnimationFrame.
  • Déplacez le code JavaScript de longue durée du thread principal vers les Web Workers.
  • Utilisez des micro-tâches pour apporter des modifications DOM sur plusieurs cadres.
  • Utilisez la chronologie et le profileur JavaScript des outils pour les développeurs Chrome 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 code JavaScript s'exécutera au début d'un frame est d'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 les exemples peuvent utiliser setTimeout ou setInterval pour effectuer des modifications visuelles telles que des animations. Le problème est que le rappel s'exécute à un moment donné dans le frame, peut-être à la fin, ce qui peut souvent nous faire manquer un frame, ce qui entraîne des à-coups.

setTimeout provoque l'absence d'un frame dans le navigateur.

En fait, jQuery utilisait setTimeout pour son comportement animate. Il a été modifié 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éduire la complexité ou utiliser des nœuds de calcul Web

JavaScript s'exécute sur le thread principal du navigateur, en plus des calculs de style, de la mise en page et, dans de nombreux cas, de la peinture. Si votre code JavaScript s'exécute pendant une longue période, il bloque ces autres tâches, ce qui peut entraîner des pertes de frames.

Vous devez être stratégique quant au moment où JavaScript s'exécute et pendant combien de temps. Par exemple, si vous utilisez une animation telle que le défilement, vous devriez idéalement essayer de limiter votre code JavaScript à 3 à 4 ms. Au-delà, vous risquez de prendre trop de temps. Si vous êtes dans une période d'inactivité, vous pouvez vous permettre d'être plus détendu quant au temps que cela prend.

Dans de nombreux cas, vous pouvez déplacer le travail de calcul pur vers des Web Workers, par exemple s'il ne nécessite pas d'accès au DOM. La manipulation ou la traversée des données, comme le tri ou la recherche, sont souvent adaptées à 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...
});

Ce modèle ne convient pas à toutes les tâches: les Web Workers n'ont pas accès au DOM. Si votre travail doit se trouver sur le thread principal, envisagez une approche par lot, dans laquelle vous segmentez la tâche plus importante en micro-tâches, chacune ne prenant pas plus de quelques millisecondes, et exécutées dans des gestionnaires requestAnimationFrame pour chaque frame.

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, soit en utilisant un indicateur de progression ou d'activité. Dans tous les cas, cette approche libère le thread principal de votre application, ce qui lui permet de rester réactif aux interactions utilisateur.

Connaître la "taxe sur les cadres" de votre code 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 par frame. Cela est particulièrement important lorsque vous effectuez des animations critiques pour les performances, comme les transitions 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. Vous obtenez généralement des enregistrements de bas niveau comme suit:

Enregistrement des performances dans Chrome DevTools

La section Principale fournit un graphique en forme de flamme des appels JavaScript afin que vous puissiez analyser exactement quelles fonctions ont été appelées et la durée de chacune.

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

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

Éviter de micro-optimiser 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 rapidement qu'une autre, par exemple que demander l'offsetTop d'un élément est plus rapide que de calculer getBoundingClientRect(). Toutefois, vous n'appellerez presque jamais des fonctions comme celles-ci 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 gagnerez que quelques fractions de milliseconde.

Si vous créez un jeu ou une application gourmande en calcul, vous faites probablement exception à ces conseils, car vous devrez généralement adapter de nombreux calculs dans un seul frame. Dans ce cas, tout est utile.

En résumé, vous devez être très prudent avec les micro-optimisations, car elles ne correspondent généralement pas au type d'application que vous créez.