Violation La tâche JavaScript longue durée a pris xx ms

331

Récemment, j'ai reçu 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 n'ai aucune idée d'où cela vient. Cela n'était jamais arrivé auparavant. Soudain, il est apparu lorsque quelqu'un d'autre s'est impliqué dans le projet. Comment savoir quel fichier / fonction provoque cet avertissement? J'ai cherché la réponse, mais surtout la solution pour la résoudre. Je ne peux pas le résoudre si je ne peux même pas trouver la source du problème.

Dans ce cas, l'avertissement n'apparaît que 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'obtiens même l'erreur de jquery.min.js:

[Violation] Handler took 231ms of runtime (50ms allowed)            jquery.min.js:2
procatmer
la source
Où voyez-vous cet avertissement? Vous ne dites pas dans quel environnement vous travaillez. En supposant un navigateur, mais lequel, etc.?
Sami Kuhmonen
3
@SamiKuhmonen désolé pour cela, j'ai mis à jour ma question. j'ai utilisé Chrome. je n'ai trouvé aucune erreur similaire sur Edge.
procatmer
8
Je voulais juste ajouter que ce message d'avertissement, introduit fin 2016, peut également apparaître en raison des extensions que vous avez peut-être installées dans Chrome. Il est facile de vérifier cela en testant en mode privé.
Fer
1
Cliquer sur le lien de droite, vous indiquant le script où les violations se produisent, vous amènera à l'endroit du code où cela se produit.
bluehipy
J'utilise Ionic 4 (Angular 8), mon code fonctionnait bien, tout à coup ce type de violation a commencé à apparaître - il n'y a plus de données affichées dans ma liste maintenant?
Kapil Raghuwanshi

Réponses:

278

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

Chrome 57 a activé `` masquer les violations '' par défaut. Pour les réactiver, vous devez activer les filtres et décocher la case «masquer les violations».

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

Je pense qu'il est plus probable que vous ayez mis à jour vers Chrome 56. Cet avertissement est une merveilleuse nouvelle fonctionnalité, à mon avis, veuillez la désactiver uniquement si vous êtes désespéré et votre évaluateur vous prendra des marques. Les problèmes sous-jacents sont présents dans les autres navigateurs, mais les navigateurs ne vous disent tout simplement pas qu'il y a un problème. Le ticket Chromium est mais il n'y a pas vraiment de discussion intéressante à ce sujet.

Ces messages sont des avertissements au lieu d'erreurs car cela ne va pas vraiment causer de problèmes majeurs. Cela peut entraîner la perte d'images ou provoquer une expérience moins fluide.

Ils méritent cependant d'être étudiés et corrigés pour améliorer la qualité de votre application. Pour ce faire, faites attention aux circonstances dans lesquelles les messages apparaissent et effectuez des tests de performances pour déterminer où le problème se produit. La façon la plus simple de démarrer les tests de performances 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 devenir plus avancé, vous pouvez également utiliser le profileur de Chrome ou utiliser une bibliothèque d'analyse comparative comme celle-ci .

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

  1. Supprimez tout ou partie de cette tâche qui peut être inutile
  2. Découvrez comment effectuer la même tâche plus rapidement
  3. Divisez le code en plusieurs étapes asynchrones

(1) et (2) peuvent être difficiles ou impossibles, mais c'est parfois très facile et devrait être vos premiers essais. 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 .

voltrevo
la source
16
Juste une suggestion, au lieu d'utiliser performance.now(), vous pouvez utiliser console.time( developer.mozilla.org/en-US/docs/Web/API/Console/time ) console.time('UniquetLabelName') ....code here.... console.timeEnd('UniqueLabelName')
denislexic
@denislexic je suppose que oui. Je ne sais pas quelle valeur cela ajoute vraiment. Je dirais que l'apprentissage de l'opération sous-jacente consistant à obtenir l'heure actuelle et à en tirer parti est plus précieux.
voltrevo
34
Excellente réponse, voltrevo! Ma question est, si un code comme celui-ci est une violation, de quoi s'agit-il exactement? Il doit y avoir une sorte de norme que Google applique, mais cette norme est-elle documentée publiquement quelque part?
Bungler
1
@Bungler Dunno, j'aimerais savoir s'il existe également des lignes directrices auxquelles il fait référence.
voltrevo
4
@Bungler Je peux seulement deviner que cela signifie que le code qui anime est en violation de fournir au moins 60 images par seconde et donc de donner une mauvaise expérience utilisateur. .
user895400
90

Ce ne sont que des avertissements, comme tout le monde l'a mentionné. Cependant, si vous souhaitez résoudre ces problèmes (ce que vous devriez), vous devez d'abord identifier la cause de l'avertissement. Il n'y a aucune 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:

Ce qui force la mise en page / la refusion

Toutes les propriétés ou méthodes ci-dessous, lorsqu'elles sont demandées / appelées en JavaScript, déclencheront le navigateur pour calculer de manière synchrone le style et la mise en page *. Ceci est également appelé refusion ou thrashing de mise en page , et est un goulot d'étranglement des performances.

Élément

Mesures de la boîte
  • elem.offsetLeft, elem.offsetTop, elem.offsetWidth, elem.offsetHeight,elem.offsetParent
  • elem.clientLeft, elem.clientTop, elem.clientWidth,elem.clientHeight
  • elem.getClientRects(), elem.getBoundingClientRect()
Faire défiler des trucs
  • elem.scrollBy(), elem.scrollTo()
  • elem.scrollIntoView(), elem.scrollIntoViewIfNeeded()
  • elem.scrollWidth, elem.scrollHeight
  • elem.scrollLeft, elem.scrollTopégalement, en les définissant
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 recalcul du style ( source )

window.getComputedStyle() forcera également la mise en page si l'une des conditions suivantes est vraie:

  1. L'élément est dans un arbre d'ombre
  2. Il existe des requêtes multimédias (liées aux fenêtres). Plus précisément, l' un des éléments suivants: ( la 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'une des suivantes: ( source )
    • height, width * top, right, bottom, left * margin[ -top, -right, -bottom, -leftOu raccourci ] que si la marge est fixée. * padding[ -top, -right, -bottom, -left, Ou raccourci ] seulement si le rembourrage est fixé. * transform, transform-origin, perspective-origin * translate, rotate, scale * webkit-filter, backdrop-filter * motion-path, motion-offset, motion-rotation * x, y, rx,ry

la fenêtre

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

Formes

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

Événements de souris

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

document

  • doc.scrollingElement force uniquement le style

Intervalle

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

SVG

contenteditable

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

Vérifiez plus ici .

En outre, voici le code source Chromium du problème d'origine et une discussion sur une API de performances pour les avertissements.


Modifier: il existe également un article sur la façon de minimiser la redistribution de la mise en page sur PageSpeed ​​Insight by Google . Il explique ce qu'est la refusion du navigateur:

Redistribution est le nom du processus du navigateur Web pour recalculer les positions et les géométries des éléments dans le document, dans le but de restituer une partie ou la totalité du document. Étant donné que la refusion est une opération bloquant l'utilisateur dans le navigateur, il est utile pour les développeurs de comprendre comment améliorer le temps de refusion et également 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 la refusion. temps. Parfois, la redistribution d'un élément unique dans le document peut nécessiter une redistribution de ses éléments parents et également de tous les éléments qui le suivent.

De plus, il explique comment le minimiser:

  1. Réduisez la profondeur DOM inutile. Les modifications à un niveau de l'arborescence DOM peuvent entraîner des modifications à tous les niveaux de l'arborescence, jusqu'à la racine et jusqu'aux descendants des nœuds modifiés. Cela conduit à passer plus de temps à effectuer 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 hors du flux. Utilisez position absolue ou position fixe pour y parvenir.
  4. Évitez les sélecteurs CSS complexes inutiles - les sélecteurs descendants en particulier - qui nécessitent plus de puissance CPU pour effectuer la correspondance des sélecteurs.
Noob
la source
1
Plus d'informations: le code source Chromium du problème d'origine et une discussion sur une API de performances pour les avertissements.
robocat
1
Selon ce qui précède, la simple lecture de element.scrollTop déclenche une redistribution. Cela me semble être un phénomène contre-intuitif. Je peux comprendre pourquoi la définition de element.scrollTop déclencherait une redistribution, mais simplement en lisant sa valeur? Quelqu'un peut-il expliquer davantage pourquoi c'est le cas, si c'est effectivement le cas?
David Edwards
29

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, regardez la moitié que vous avez commentée!

  • Utilisez-vous un système de contrôle de version (par exemple, Git)? Si c'est le cas, git checkoutcertains de vos engagements 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.

therobinkim
la source
Merci pour votre réponse. j'ai supprimé la moitié et même exclu mon fichier .js principal du projet. d'une manière ou d'une autre, l'erreur s'est toujours produite. c'est pourquoi je suis tellement frustrant à ce sujet. et oui, j'utilise git. je viens de réaliser cette erreur aujourd'hui. il y a eu beaucoup de commits depuis que c'est devenu un projet de groupe. pourrait faire une vérification approfondie. Merci encore pour les idées.
procatmer
@procatmer utilise la même stratégie pour trouver le git commit. Par exemple, si j'avais 10 commits (A, B, C, D, E, F, G, H, I, J) où A était le plus ancien, je git checkout Everrais si le problème existe déjà. Si oui, je continuerai de chercher le problème dans la première moitié des commits. Sinon, je cherche le problème dans la seconde moitié.
therobinkim
1
@procatmer Aussi, si vous avez omis votre .jsfichier principal et que le problème persiste ... il pourrait s'agir d'une bibliothèque que vous avez importée via une <script src="...">balise! Peut-être que quelque chose ne vaut pas la peine de s'inquiéter (d'autant plus que ce n'est qu'un avertissement)?
therobinkim
1
j'ai enfin trouvé où est le problème. j'ai utilisé votre deuxième idée pour suivre les changements. et oui, le problème vient d'un .jsfichier externe . apparemment, cela compte. ça ralentit mon site assez important. de toute façon, merci encore pour vos réponses et vos idées.
procatmer
2
Vous pouvez utiliser git bisect pour appliquer la recherche binaire. Je pense que c'est juste pour trouver des bogues.
pietrovismara
12

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

Là, vous pouvez vérifier diverses fonctions qui ont pris du temps à s'exécuter. Dans mon cas, celui qui correspondait aux 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 d'un code d'extension ou du vôtre. (Si c'est le vôtre, vous avez trouvé la source de votre problème.)

Matt Leonowicz
la source
Non, je n'ai pas AdBlock et je l'obtiens toujours dans la console.
Nikola Stojaković
Essayez de l'analyser avec l'onglet Performance et recherchez la source des fonctions qui s'exécutent depuis longtemps. Cela pourrait être n'importe quoi, mais c'est un moyen potentiel d'identifier la source du problème.
Matt Leonowicz
6

Regardez dans la console Chrome sous l'onglet Réseau et trouvez les scripts qui prennent le plus de temps à charger.

Dans mon cas, il y avait un ensemble de scripts complémentaires Angular que j'avais inclus mais 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 prenait plus de temps 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 «Tâche de longue durée» sur une nouvelle application Web qui n'avait pratiquement aucune fonctionnalité. L'erreur s'est arrêtée immédiatement lors de la suppression.

Ma meilleure supposition est que ces modules complémentaires angulaires cherchaient de manière récursive dans des sections de plus en plus profondes du DOM pour leurs balises de démarrage - n'en trouvant aucun, ils devaient parcourir l'intégralité du DOM avant de quitter, ce qui a pris plus de temps que prévu Chrome - d'où l'avertissement.

Jordan Reddick
la source
6

J'ai trouvé la racine de ce message dans mon code, qui a cherché et caché ou montré 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 des performances (profileur) montre l'événement prenant environ 60 ms: Refusion de recalcul de la disposition du profileur de performances de chrome

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 de performance (profileur) montre maintenant l'événement prenant environ 1 ms: Profileur chrome foncé

Et je pense que la recherche fonctionne plus rapidement maintenant (229 nœuds).

Vitaly Zdanevich
la source
3
En résumé, en recevant la violation, vous avez pu optimiser votre code et il fonctionne mieux maintenant.
Utilisateur qui n'est pas un utilisateur
3

J'ai trouvé une solution dans le code source d'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); };

Implémentation simple, mais intelligente.

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


Usage:

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

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

wf9a5m75
la source
2

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

Cochez la case Violations dans la console Chrome 56

zhaoming
la source
1

Il s'agit d'une erreur de violation de Google Chrome qui indique lorsque le Verboseniveau de journalisation est activé.

Exemple de message d'erreur:

capture d'écran de l'avertissement

Explication:

Redistribution est le nom du processus du navigateur Web pour recalculer les positions et les géométries des éléments dans le document, dans le but de restituer une partie ou la totalité du document. Étant donné que la refusion est une opération bloquant l'utilisateur dans le navigateur, il est utile pour les développeurs de comprendre comment améliorer le temps de refusion et également 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 la refusion. temps. Parfois, la redistribution d'un élément unique dans le document peut nécessiter une redistribution de ses éléments parents et également de tous les éléments qui le suivent.

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

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

Paul-Sebastian Manole
la source
0

L'ajout de mes idées ici car ce fil était la question "aller à" stackoverflow sur le sujet.

Mon problème était dans une application Material-UI (premiers stades)

  • l'emplacement du fournisseur de thème personnalisé était la cause

quand j'ai fait quelques calculs forçant le rendu de la page (un composant, "afficher les résultats", dépend de ce qui est défini dans les autres, "sections d'entrée").

Tout allait bien jusqu'à ce que je mette à jour l '"état" qui force le "composant de résultats" à se restituer. Le problème principal ici était que j'avais un thème matériel-interface utilisateur ( https://material-ui.com/customization/theming/#a-note-on-performance ) dans le même moteur de rendu (App.js / return ..) comme "composant de résultats", SummaryAppBarPure

La solution consistait à élever le ThemeProvider d'un niveau (Index.js) et à envelopper le composant App ici, ne forçant ainsi pas le ThemeProvider à recalculer et à dessiner / disposition / redistribution.

avant

dans App.js:

  return (
    <>
      <MyThemeProvider>
      <Container className={classes.appMaxWidth}>

        <SummaryAppBarPure
//...

dans index.js

ReactDOM.render(
  <React.StrictMode>
      <App />
//...

après

dans App.js:

return (
    <>
      {/* move theme to index. made reflow problem go away */}
      {/* <MyThemeProvider> */}
      <Container className={classes.appMaxWidth}>

        <SummaryAppBarPure
//...

dans index.js

ReactDOM.render(
  <React.StrictMode>
    <MyThemeProvider>
      <App />
//...
JimiSweden
la source
-2

La redistribution forcée 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, mais pas sur un navigateur classique.

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

Ce n'est pas très important, mais je le répète, le problème survient lorsque vous appelez une fonction plusieurs fois, et non lorsque la fonction prend plus de 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 s'il génère toujours l'erreur.
  2. Si un deuxième script provoque l'erreur, utilisez un setTimeOutbasé sur la durée de la violation.
Cherif
la source
Ce n'est pas une solution. Il vaut mieux laisser une suggestion en commentaire de la question d'origine.
Paul-Sebastian Manole
-6

Ce n'est pas une erreur juste un simple message. Pour exécuter ce message, changez
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">(exemple)
en
<!DOCTYPE html>(la source Firefox s'y attend)

Le message a été affiché dans Google Chrome 74 et Opera 60. Après avoir changé, il était clair, 0 verbeux.
Une approche solution

Gerdixx
la source
5
Juste quelques conseils: votre réponse n'a rien à voir avec les questions. Corrigez votre réponse ou supprimez-la. La question était "pourquoi la console du navigateur Chrome affiche-t-elle un avertissement de violation". La réponse est qu'il s'agit d'une fonctionnalité des nouveaux navigateurs Chrome où il vous avertit si la page Web provoque un redistribution excessive du navigateur lors de l'exécution de JS. Veuillez vous référer à cette ressource de Google pour plus d'informations.
Paul-Sebastian Manole