web-dev-qa-db-fra.com

Violation La longue tâche JavaScript a pris xx ms

Récemment, j'ai eu ce genre d'avertissement, et c'est la première fois que je l'obtiens:

[Violation] Long running JavaScript task took 234ms
[Violation] Forced reflow while executing JavaScript took 45ms

Je travaille sur un projet de groupe et je ne sais pas d'où ça vient. Ce n'est jamais arrivé avant. Soudain, il est apparu que quelqu'un d'autre s'était impliqué dans le projet. Comment trouver quel fichier/quelle fonction provoque cet avertissement? Je cherchais la réponse, mais surtout la solution pour y remédier. Je ne peux pas le résoudre si je ne trouve même pas la source du problème.

Dans ce cas, l'avertissement apparaît uniquement sur Chrome. J'ai essayé d'utiliser Edge, mais je n'ai reçu aucun avertissement similaire, et je ne l'ai pas encore testé sur Firefox.

J'ai même l'erreur de jquery.min.js:

[Violation] Handler took 231ms of runtime (50ms allowed)            jquery.min.js:2
255
procatmer

Mise à jour : Chrome 58+ a masqué ces messages et d'autres messages de débogage par défaut. Pour les afficher, cliquez sur la flèche à côté de "Info" et sélectionnez "Verbose".

Chrome 57 activait par défaut "masquer les violations". Pour les réactiver, vous devez activer les filtres et décocher la case "masquer les violations".

soudain, il apparaît quand quelqu'un d'autre impliqué dans le projet

Je pense qu'il est plus probable que vous ayez mis à jour Chrome 56. Cet avertissement est une nouveauté merveilleuse. À mon avis, désactivez-le uniquement si vous êtes désespéré et que votre évaluateur vous enlèvera des marques. Les problèmes sous-jacents existent dans les autres navigateurs, mais ceux-ci ne vous disent pas qu'il y a un problème. Le billet Chromium est ici mais il n’ya pas vraiment de discussion intéressante à ce sujet.

Ces messages sont des avertissements au lieu d'erreurs car ils ne vont pas vraiment causer de problèmes majeurs. Cela risquerait de faire tomber les images ou de rendre l’utilisation moins fluide.

Toutefois, ils valent la peine d’être étudiés et corrigés pour améliorer la qualité de votre application. Pour ce faire, il faut être attentif aux circonstances dans lesquelles les messages apparaissent et effectuer des tests de performances pour cerner le problème. Le moyen le plus simple de démarrer les tests de performance consiste à insérer du code comme celui-ci:

function someMethodIThinkMightBeSlow() {
    const startTime = performance.now();

    // Do the normal stuff for this function

    const duration = performance.now() - startTime;
    console.log(`someMethodIThinkMightBeSlow took ${duration}ms`);
}

Si vous souhaitez être plus avancé, vous pouvez également utiliser le profileur de Chrome , ou utiliser une bibliothèque d'analyse comparative telle que celle-ci .

Une fois que vous avez trouvé du code qui prend beaucoup de temps (50 ms est le seuil de Chrome), vous avez plusieurs options:

  1. Découpez une partie ou la totalité de cette tâche qui pourrait être inutile
  2. Comprendre comment faire la même tâche plus rapidement
  3. Diviser le code en plusieurs étapes asynchrones

(1) et (2) peuvent être difficiles ou impossibles, mais c'est parfois très facile et devraient être vos premières tentatives. Si nécessaire, il devrait toujours être possible de le faire (3). Pour ce faire, vous utiliserez quelque chose comme:

setTimeout(functionToRunVerySoonButNotNow);

ou

// This one is not available natively in IE, but there are polyfills available.
Promise.resolve().then(functionToRunVerySoonButNotNow);

Vous pouvez en savoir plus sur la nature asynchrone de JavaScript ici .

230
voltrevo

Ce ne sont que des avertissements comme tout le monde l'a mentionné. Toutefois, si vous souhaitez résoudre ces problèmes (ce que vous devriez faire), vous devez d'abord identifier la cause de l'avertissement. Il n'y a pas une seule raison pour laquelle vous pouvez obtenir un avertissement de refusion forcée. Quelqu'un a créé une liste pour certaines options possibles. Vous pouvez suivre la discussion pour plus d'informations.
Voici l'essentiel des raisons possibles:

Quelles forces layout/refusion

Toutes les propriétés ou méthodes ci-dessous, lorsqu'elles sont demandées/appelées en JavaScript, obligeront le navigateur à calculer simultanément le style et la mise en page *. Cela s'appelle également redistribution ou mise en page dynamique et constitue un goulot d'étranglement courant en termes de performances.

Élément

Boîte de mesures
  • elem.offsetLeft, elem.offsetTop, elem.offsetWidth, elem.offsetHeight, elem.offsetParent
  • elem.clientLeft, elem.clientTop, elem.clientWidth, elem.clientHeight
  • elem.getClientRects(), elem.getBoundingClientRect()
Faites défiler les choses
  • elem.scrollBy(), elem.scrollTo()
  • elem.scrollIntoView(), elem.scrollIntoViewIfNeeded()
  • elem.scrollWidth, elem.scrollHeight
  • elem.scrollLeft, elem.scrollTop également, les régler
Concentrer
  • elem.focus() peut déclencher une mise en page forcée double ( source )
Aussi…
  • elem.computedRole, elem.computedName
  • elem.innerText ( source )

getComputedStyle

window.getComputedStyle() forcera généralement le style à recalculer ( source )

window.getComputedStyle() forcera la disposition, si l'une des conditions suivantes est vraie:

  1. L'élément est dans un arbre d'ombre
  2. Il existe des requêtes de médias (liées à la fenêtre d'affichage). Plus précisément, l'un des éléments suivants: ( source ) * min-width, min-height, max-width, max-height, width, height * aspect-ratio, min-aspect-ratio, max-aspect-ratio
    • device-pixel-ratio, resolution, orientation
  3. La propriété demandée est l’un des suivants: ( source )
    • height, width * top, right, bottom, left * margin [-top, -right, -bottom, -left, ou raccourci] uniquement si la marge est fixe. * padding [-top, -right, -bottom, -left, ou en abrégé] uniquement si le remplissage est corrigé. * transform, transform-Origin, perspective-Origin * translate, rotate, scale * webkit-filter, backdrop-filter * motion-path, motion-offset, motion-rotation * x, y, rx, ry

fenêtre

  • window.scrollX, window.scrollY
  • window.innerHeight, window.innerWidth
  • window.getMatchedCSSRules() ne force que le style

Formes

  • inputElem.focus()
  • inputElem.select(), textareaElem.select() ( source )

Événements de souris

  • mouseEvt.layerX, mouseEvt.layerY, mouseEvt.offsetX, mouseEvt.offsetY ( source )

document

  • doc.scrollingElement ne force que le style

Gamme

  • range.getClientRects(), range.getBoundingClientRect()

SVG

contenteditable

  • Beaucoup de choses,… y compris la copie d'une image dans le presse papier ( source )

Vérifiez plus ici .

En outre, voici le code source Chromium du version originale et un discussion sur une API de performance pour les avertissements.


Edit: Il existe également un article sur la façon de minimiser la redistribution de la présentation sur PageSpeed ​​Insight de Google . Il explique ce qu'est le reflow du navigateur:

Redistribution est le nom du processus de navigateur Web permettant de recalculer les positions et les géométries des éléments du document afin de restituer tout ou partie du document. Le refusion étant une opération qui bloque l'utilisateur dans le navigateur, il est utile pour les développeurs de savoir comment améliorer le temps de refusion et de comprendre les effets de diverses propriétés de document (profondeur DOM, efficacité des règles CSS, différents types de changements de style) sur le refoulement. temps. Parfois, refondre un seul élément dans le document peut nécessiter de refondre ses éléments parents ainsi que tous les éléments qui le suivent.

En outre, il explique comment le minimiser:

  1. Réduisez les profondeurs de DOM inutiles. Les modifications apportées à un niveau de l'arborescence DOM peuvent entraîner des modifications à tous les niveaux de l'arborescence, de la racine à la racine, jusqu'aux enfants du nœud modifié. Cela conduit à plus de temps consacré à la refusion.
  2. Réduisez les règles CSS et supprimez les règles CSS inutilisées.
  3. Si vous apportez des modifications de rendu complexes telles que des animations, faites-le en dehors du flux. Utilisez position-absolute ou position-fixed pour accomplir cela.
  4. Évitez les sélecteurs CSS complexes et inutiles - les sélecteurs descendants en particulier - qui nécessitent davantage de puissance de calcul pour effectuer la correspondance de sélecteur.
67
noob

Quelques idées:

  • Supprimez la moitié de votre code (peut-être en le commentant).

    • Le problème est-il toujours là? Génial, vous avez réduit les possibilités! Répéter.

    • Le problème n'est-il pas là? Ok, regarde la moitié que tu as commentée!

  • Utilisez-vous un système de contrôle de version (par exemple, Git)? Si tel est le cas, git checkout certains de vos commits les plus récents. Quand le problème a-t-il été introduit? Regardez le commit pour voir exactement quel code a changé lorsque le problème est arrivé pour la première fois.

28
therobinkim

Dans mon cas, j'ai découvert que cela était en fait causé par un code provenant d'une extension. Pour moi, l'extension était AdBlock.

Pour identifier la source du problème, exécutez votre application et enregistrez-la dans onglet Performances de Chrome.

Vous pouvez y vérifier diverses fonctions qui ont pris beaucoup de temps. Dans mon cas, la corrélation avec les avertissements dans la console provenait d'un fichier chargé par l'extension AdBlock, mais cela pourrait être autre chose dans votre cas.

Vérifiez ces fichiers et essayez d’identifier s’il s’agit du code d’une extension ou du vôtre. (Si c'est le vôtre, alors vous avez trouvé la source de votre problème.)

8
Matt Leonowicz

Recherchez dans la console Chrome sous l'onglet Réseau et recherchez les scripts dont le chargement est le plus long.

Dans mon cas, il y avait un ensemble de Angular scripts additionnels que j'avais inclus mais que je n'avais pas encore utilisés dans l'application:

<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.8/angular-ui-router.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-utils/0.1.1/angular-ui-utils.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular-animate.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular-aria.min.js"></script>

Il s’agissait des seuls fichiers JavaScript dont le chargement a été plus long que le temps spécifié par l’erreur "Long Running Task".

Tous ces fichiers s’exécutent sur mes autres sites Web sans générer d’erreurs, mais j’obtenais cette erreur "Long Running Task" sur une nouvelle application Web qui ne comportait pratiquement aucune fonctionnalité. L'erreur s'est arrêtée immédiatement après la suppression.

Ma meilleure hypothèse est que ces additifs Angular cherchaient récursivement des sections de plus en plus profondes du DOM dans leurs balises de départ - sans en trouver, ils devaient parcourir l'intégralité du DOM avant de sortir, ce qui prenait plus de temps que Chrome attend - d'où l'avertissement.

5
Jordan Reddick

Ceci a été ajouté dans la version bêta de Chrome 56, bien que cela ne figure pas dans ce journal des modifications du blog Chromium: avertissement "Non sécurisé" de Chrome 56, Web Bluetooth et CSS position: sticky

Vous pouvez masquer cela dans la barre de filtre de la console avec la case à cocher Masquer les violations .

4
Bastien

J'ai trouvé la racine de ce message dans mon code, qui cherchait et masquait ou montrait des nœuds (hors ligne). C'était mon code:

search.addEventListener('keyup', function() {
    for (const node of nodes)
        if (node.innerText.toLowerCase().includes(this.value.toLowerCase()))
            node.classList.remove('hidden');
        else
            node.classList.add('hidden');
});

L'onglet Performances (profileur) indique que l'événement dure environ 60 ms: Chromium performance profiler layout recalculation reflow

Maintenant:

search.addEventListener('keyup', function() {
    const nodesToHide = [];
    const nodesToShow = [];
    for (const node of nodes)
        if (node.innerText.toLowerCase().includes(this.value.toLowerCase()))
            nodesToShow.Push(node);
        else
            nodesToHide.Push(node);

    nodesToHide.forEach(node => node.classList.add('hidden'));
    nodesToShow.forEach(node => node.classList.remove('hidden'));
});

L'onglet Performances (profileur) affiche maintenant l'événement qui dure environ 1 ms: Chromium profiler dark

Et je pense que la recherche marche plus vite maintenant (229 nœuds).

3
Vitaly Zdanevich

Si vous utilisez Chrome Canary (ou bêta), cochez simplement l'option "Masquer les violations".

Hide Violations Checkbox in Chrome 56 Console

3
zhaoming

J'ai trouvé une solution dans le code source Apache Cordova. Ils implémentent comme ceci:

var resolvedPromise = typeof Promise == 'undefined' ? null : Promise.resolve();
var nextTick = resolvedPromise ? function(fn) { resolvedPromise.then(fn); } : function(fn) { setTimeout(fn); };

Une mise en œuvre simple, mais intelligente.

Sur Android 4.4, utilisez Promise. Pour les anciens navigateurs, utilisez setTimeout()


Usage:

nextTick(function() {
  // your code
});

Après avoir inséré ce code astuce, tous les messages d’avertissement ont disparu.

3
wf9a5m75

Il s'agit d'une erreur de violation de Google Chrome qui s'affiche lorsque le niveau de journalisation Verbose est activé.

Exemple de message d'erreur:

screenshot of the warning

Explication:

Redistribution est le nom du processus de navigateur Web permettant de recalculer les positions et les géométries des éléments du document afin de restituer tout ou partie du document. Le refusion étant une opération qui bloque l'utilisateur dans le navigateur, il est utile pour les développeurs de savoir comment améliorer le temps de refusion et de comprendre les effets de diverses propriétés de document (profondeur DOM, efficacité des règles CSS, différents types de changements de style) sur le refoulement. temps. Parfois, refondre un seul élément dans le document peut nécessiter de refondre ses éléments parents ainsi que tous les éléments qui le suivent.

Article original: Réduire au minimum la redistribution du navigateur par Lindsey Simon, développeur UX, publiée sur developers.google.com.

Et c'est le lien Google Chrome vous donne dans le profileur de performances, sur les profils de présentation (les régions mauves), pour plus d'informations sur l'avertissement.

0

le refoulement forcé se produit souvent lorsque vous avez une fonction appelée plusieurs fois avant la fin de l'exécution.

Par exemple, vous pouvez avoir le problème sur un smartphone et non sur un navigateur classique.

Je suggère d'utiliser un setTimeout pour résoudre le problème.

Ce n’est pas très important, mais je le répète, le problème se pose lorsque vous appelez une fonction plusieurs fois, et non lorsque la fonction est supérieure à 50 ms. Je pense que vous vous trompez dans vos réponses.

  1. Désactivez les appels 1 par 1 et rechargez le code pour voir si le produit est en erreur.
  2. Si un second script provoque l'erreur, utilisez un setTimeOut basé sur la durée de la violation.
0
Cherif

1 arrivé à la console

2 Cliquez sur l'icône de filtre à proximité (case à cocher "Conserver le journal").

3 Cochez la case "Masquer la violation"


EDIT

Cette fonctionnalité a été supprimée de Chrome 58.

Modifiez le menu déroulant du niveau de journalisation sur Verbose pour voir les violations.

0
Sajan