Violación La tarea de JavaScript de larga ejecución tomó xx ms

331

Recientemente, recibí este tipo de advertencia, y esta es la primera vez que la recibo:

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

Estoy trabajando en un proyecto grupal y no tengo idea de dónde viene esto. Esto nunca pasó antes. De repente, apareció cuando alguien más se involucró en el proyecto. ¿Cómo encuentro qué archivo / función causa esta advertencia? He estado buscando la respuesta, pero principalmente sobre la solución sobre cómo resolverla. No puedo resolverlo si ni siquiera puedo encontrar la fuente del problema.

En este caso, la advertencia solo aparece en Chrome. Intenté usar Edge, pero no recibí ninguna advertencia similar, y aún no lo he probado en Firefox.

Incluso recibo el error de jquery.min.js:

[Violation] Handler took 231ms of runtime (50ms allowed)            jquery.min.js:2
procatmer
fuente
¿Dónde ves esta advertencia? No dice en qué entorno está trabajando. Suponiendo un navegador, pero ¿cuál, etc.?
Sami Kuhmonen
3
@SamiKuhmonen lo siento, he actualizado mi pregunta. Yo usé Chrome. No encontré ningún error similar en Edge.
Procatmer
8
Solo quería agregar que este mensaje de advertencia, presentado a fines de 2016, también puede aparecer debido a las extensiones que haya instalado en Chrome. Es fácil verificar eso probando en modo privado.
Fer
1
Al hacer clic en el enlace del lado derecho, que le indica el guión donde ocurren las violaciones, lo llevará al lugar en el código donde sucede.
bluehipy
Estoy usando Ionic 4 (Angular 8), mi código funcionaba bien, de repente, este tipo de violación comenzó a aparecer, ¿no hay datos que se muestren en mi lista ahora?
Kapil Raghuwanshi

Respuestas:

278

Actualización : Chrome 58+ ocultó estos y otros mensajes de depuración de forma predeterminada. Para mostrarlos, haga clic en la flecha al lado de 'Información' y seleccione 'Detallado'.

Chrome 57 activó 'ocultar violaciones' de forma predeterminada. Para volver a activarlos, debe habilitar los filtros y desmarcar la casilla 'ocultar violaciones'.

de repente aparece cuando alguien más involucrado en el proyecto

Creo que es más probable que haya actualizado a Chrome 56. Esta advertencia es una nueva característica maravillosa, en mi opinión, solo desactívela si está desesperado y su asesor le quitará las marcas. Los problemas subyacentes están allí en los otros navegadores, pero los navegadores simplemente no le dicen que hay un problema. El boleto de Chromium está aquí, pero en realidad no hay ninguna discusión interesante al respecto.

Estos mensajes son advertencias en lugar de errores porque en realidad no va a causar problemas importantes. Puede hacer que los cuadros se caigan o causar una experiencia menos fluida.

Sin embargo, vale la pena investigar y corregir para mejorar la calidad de su aplicación. La forma de hacerlo es prestando atención a las circunstancias en que aparecen los mensajes y realizando pruebas de rendimiento para reducir dónde se produce el problema. La forma más sencilla de comenzar las pruebas de rendimiento es insertar un código como este:

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 desea avanzar más, también puede usar el generador de perfiles de Chrome o utilizar una biblioteca de evaluación comparativa como esta .

Una vez que haya encontrado algún código que tarde mucho tiempo (50 ms es el umbral de Chrome), tiene un par de opciones:

  1. Elimina parte / toda esa tarea que puede ser innecesaria
  2. Descubre cómo hacer la misma tarea más rápido
  3. Divide el código en múltiples pasos asincrónicos

(1) y (2) pueden ser difíciles o imposibles, pero a veces es realmente fácil y deberían ser tus primeros intentos. Si es necesario, siempre debe ser posible hacer (3). Para hacer esto usarás algo como:

setTimeout(functionToRunVerySoonButNotNow);

o

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

Puede leer más sobre la naturaleza asincrónica de JavaScript aquí .

voltrevo
fuente
16
Solo una sugerencia, en lugar de usar performance.now(), podría usar console.time( developer.mozilla.org/en-US/docs/Web/API/Console/time ) console.time('UniquetLabelName') ....code here.... console.timeEnd('UniqueLabelName')
denislexic
@denislexic supongo que sí. Sin embargo, no estoy seguro de qué valor realmente agrega. Yo diría que aprender sobre la operación subyacente de obtener el tiempo actual y aprovecharlo es más valioso.
voltrevo
34
Gran respuesta, voltrevo! Mi pregunta es, si un código como este es una violación, ¿de qué se trata exactamente? Tiene que haber algún tipo de estándar que Google esté aplicando, pero ¿está ese documento documentado públicamente en alguna parte?
Bungler
1
@Bungler Dunno, me gustaría saber si hay alguna directriz a la que se refiera también.
voltrevo
44
@Bungler Solo puedo adivinar que está diciendo que el código que está animando viola proporcionar al menos 60 cuadros por segundo y, por lo tanto, brinda una mala experiencia de usuario. .
user895400
90

Estas son solo advertencias como todos mencionaron. Sin embargo, si está interesado en resolverlos (que debería), primero debe identificar qué está causando la advertencia. No hay una sola razón por la cual puede obtener una advertencia de reflujo de fuerza. Alguien ha creado una lista para algunas opciones posibles. Puedes seguir la discusión para más información.
Aquí está la esencia de las posibles razones:

Lo que fuerza el diseño / reflujo

Todas las propiedades o métodos a continuación, cuando se solicitan / llaman en JavaScript, activarán el navegador para calcular sincrónicamente el estilo y el diseño *. Esto también se llama reflujo o distribución de diseño , y es un cuello de botella de rendimiento común.

Elemento

Métricas de caja
  • elem.offsetLeft` elem.offsetTop` elem.offsetWidth` elem.offsetHeight`elem.offsetParent
  • elem.clientLeft, elem.clientTop, elem.clientWidth,elem.clientHeight
  • elem.getClientRects(), elem.getBoundingClientRect()
Cosas de desplazamiento
  • elem.scrollBy(), elem.scrollTo()
  • elem.scrollIntoView(), elem.scrollIntoViewIfNeeded()
  • elem.scrollWidth, elem.scrollHeight
  • elem.scrollLeft, elem.scrollToptambién, configurándolos
Atención
  • elem.focus() puede desencadenar un diseño doblemente forzado ( fuente )
También…
  • elem.computedRole, elem.computedName
  • elem.innerText( fuente )

getComputedStyle

window.getComputedStyle()típicamente forzará el recalc de estilo ( fuente )

window.getComputedStyle() forzará el diseño, también, si alguno de los siguientes es cierto:

  1. El elemento está en un árbol de sombra.
  2. Hay consultas de medios (relacionadas con la ventana gráfica). Específicamente, uno de los siguientes: ( fuente ) * 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 propiedad solicitada es una de las siguientes: ( fuente )
    • height, width * top, right, bottom, left * margin[ -top, -right, -bottom, -left, O taquigrafía ] sólo si el margen es fijo. * padding[ -top, -right, -bottom, -left, O abreviada ] sólo si el relleno es fijo. * transform, transform-origin, perspective-origin * translate, rotate, scale * webkit-filter, backdrop-filter * motion-path, motion-offset, motion-rotation * x, y, rx,ry

ventana

  • window.scrollX, window.scrollY
  • window.innerHeight, window.innerWidth
  • window.getMatchedCSSRules() solo fuerza el estilo

Formas

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

Eventos de mouse

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

documento

  • doc.scrollingElement solo fuerza el estilo

Rango

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

SVG

contento

  • Montones y montones de cosas, ... incluyendo copiar una imagen al portapapeles ( fuente )

Consulta más aquí .

Además, aquí está el código fuente de Chromium del problema original y una discusión sobre una API de rendimiento para las advertencias.


Editar: También hay un artículo sobre cómo minimizar el reflujo de diseño en PageSpeed ​​Insight de Google . Explica qué es el reflujo del navegador:

Reflow es el nombre del proceso del navegador web para volver a calcular las posiciones y las geometrías de los elementos en el documento, con el fin de volver a representar parte o la totalidad del documento. Debido a que el reflujo es una operación de bloqueo del usuario en el navegador, es útil para los desarrolladores comprender cómo mejorar el tiempo de reflujo y también comprender los efectos de varias propiedades del documento (profundidad del DOM, eficiencia de la regla CSS, diferentes tipos de cambios de estilo) en el reflujo hora. A veces, el reflujo de un solo elemento en el documento puede requerir el reflujo de sus elementos principales y también de los elementos que lo siguen.

Además, explica cómo minimizarlo:

  1. Reduce la profundidad innecesaria del DOM. Los cambios en un nivel en el árbol DOM pueden causar cambios en cada nivel del árbol, hasta la raíz y hasta los elementos secundarios del nodo modificado. Esto lleva a pasar más tiempo realizando reflujo.
  2. Minimice las reglas CSS y elimine las reglas CSS no utilizadas.
  3. Si realiza cambios de representación complejos, como animaciones, hágalo fuera del flujo. Use position-absolute o position-fixed para lograr esto.
  4. Evite los selectores CSS complejos innecesarios, en particular los selectores descendientes, que requieren más potencia de CPU para hacer la selección del selector.
novato
fuente
1
Más antecedentes: el código fuente de Chromium del problema original y una discusión sobre una API de rendimiento para las advertencias.
robocat
1
De acuerdo con lo anterior, simplemente leer element.scrollTop desencadena un reflujo. Esto me parece un fenómeno contraintuitivo. Puedo entender por qué establecer element.scrollTop desencadenaría un reflujo, pero simplemente leer su valor. ¿Alguien puede explicar más por qué este es el caso, si es así?
David Edwards
29

Un par de ideas:

  • Elimina la mitad de tu código (quizás comentando).

    • ¿El problema sigue ahí? ¡Genial, has reducido las posibilidades! Repetir.

    • ¿El problema no está ahí? Ok, mira la mitad que comentaste!

  • ¿Está utilizando algún sistema de control de versiones (por ejemplo, Git)? Si es así, git checkoutalgunos de sus compromisos más recientes. ¿Cuándo se introdujo el problema? Mire la confirmación para ver exactamente qué código cambió cuando llegó el problema por primera vez.

therobinkim
fuente
Gracias por su respuesta. eliminé la mitad e incluso excluí mi archivo .js principal del proyecto. de alguna manera el error todavía ocurrió. Por eso me siento tan frustrante. y sí, estoy usando git. Acabo de darme cuenta de este error hoy. ha habido muchos compromisos desde que se convirtió en proyecto grupal. podría hacer una revisión profunda. Gracias de nuevo por las ideas.
Procatmer
@procatmer usa la misma estrategia para encontrar el git commit. Por ejemplo, si tuviera 10 commits (A, B, C, D, E, F, G, H, I, J) donde A era el más antiguo, git checkout Evería si el problema ya existe. En caso afirmativo, continuaré buscando el problema en la primera mitad de las confirmaciones. De lo contrario, busco el problema en la segunda mitad.
therobinkim
1
@procatmer Además, si omitió su .jsarchivo principal y el problema persiste ... ¡podría ser una biblioteca que trajo a través de una <script src="...">etiqueta! ¿Quizás algo por lo que no valga la pena preocuparse (especialmente porque es solo una advertencia)?
therobinkim
1
Finalmente he encontrado dónde está el problema. Usé tu segunda idea para rastrear los cambios. y sí, el problema proviene de un .jsarchivo externo . Al parecer, sí importa. ralentiza mi sitio bastante significativo. de todos modos, gracias de nuevo por sus respuestas e ideas.
Procatmer
2
Puede usar git bisect para aplicar la búsqueda binaria. Creo que es solo para encontrar errores.
pietrovismara
12

Para identificar la fuente del problema, ejecute su aplicación y regístrela en la pestaña Rendimiento de Chrome .

Allí puede verificar varias funciones que tardaron mucho en ejecutarse. En mi caso, el que se correlacionó con las advertencias en la consola fue de un archivo que fue cargado por la extensión AdBlock, pero esto podría ser algo más en su caso.

Verifique estos archivos e intente identificar si este es el código de alguna extensión o el suyo. (Si es tuyo, entonces has encontrado la fuente de tu problema).

Matt Leonowicz
fuente
No, no tengo AdBlock y todavía lo tengo en la consola.
Nikola Stojaković
Intente analizarlo con la pestaña Rendimiento y busque la fuente de las funciones que se ejecutan durante mucho tiempo. Esto podría ser cualquier cosa, pero esta es una forma potencial de identificar la fuente del problema.
Matt Leonowicz el
6

Busque en la consola de Chrome debajo de la pestaña Red y encuentre los scripts que tardan más en cargarse.

En mi caso, había un conjunto de scripts adicionales de Angular que había incluido pero que aún no había usado en la aplicación:

<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>

Estos fueron los únicos archivos JavaScript que tardaron más en cargarse que el tiempo que especificó el error "Tarea de ejecución larga".

Todos estos archivos se ejecutan en mis otros sitios web sin generar errores, pero recibí este error de "Tarea de ejecución prolongada" en una nueva aplicación web que apenas tenía funcionalidad. El error se detuvo inmediatamente al eliminarlo.

Mi mejor conjetura es que estos complementos angulares buscaban recursivamente en secciones cada vez más profundas del DOM para sus etiquetas de inicio; al no encontrar ninguna, tuvieron que atravesar todo el DOM antes de salir, lo que llevó más tiempo de lo que Chrome esperaba, por lo tanto, la advertencia.

Jordan Reddick
fuente
6

Encontré la raíz de este mensaje en mi código, que buscaba y ocultaba o mostraba nodos (sin conexión). Este fue mi código:

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');
});

La pestaña de rendimiento (generador de perfiles) muestra que el evento tarda unos 60 ms: Reflujo de recálculo del diseño del perfilador de rendimiento de cromo

Ahora:

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'));
});

La pestaña de rendimiento (generador de perfiles) ahora muestra que el evento dura aproximadamente 1 ms: Perfilador de cromo oscuro

Y siento que la búsqueda funciona más rápido ahora (229 nodos).

Vitaly Zdanevich
fuente
3
En resumen, al recibir la infracción, pudo optimizar su código y ahora funciona mejor.
Usuario que no es usuario
3

Encontré una solución en el código fuente de Apache Cordova. Implementan así:

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

Implementación simple, pero de manera inteligente.

Sobre el Android 4.4, use Promise. Para navegadores antiguos, usesetTimeout()


Uso:

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

Después de insertar este código de truco, todos los mensajes de advertencia desaparecen.

wf9a5m75
fuente
2

Si está utilizando Chrome Canary (o Beta), simplemente marque la opción 'Ocultar infracciones'.

Casilla de verificación Ocultar violaciones en Chrome 56 Console

zhaoming
fuente
1

Este es un error de violación de Google Chrome que se muestra cuando el Verbosenivel de registro está habilitado.

Ejemplo de mensaje de error:

captura de pantalla de la advertencia

Explicación:

Reflow es el nombre del proceso del navegador web para volver a calcular las posiciones y las geometrías de los elementos en el documento, con el fin de volver a representar parte o la totalidad del documento. Debido a que el reflujo es una operación de bloqueo del usuario en el navegador, es útil para los desarrolladores comprender cómo mejorar el tiempo de reflujo y también comprender los efectos de varias propiedades del documento (profundidad del DOM, eficiencia de la regla CSS, diferentes tipos de cambios de estilo) en el reflujo hora. A veces, el reflujo de un solo elemento en el documento puede requerir el reflujo de sus elementos principales y también de los elementos que lo siguen.

Artículo original: minimizar el reflujo del navegador por Lindsey Simon, UX Developer, publicado en developers.google.com.

Y este es el enlace que Google Chrome le brinda en el perfilador de rendimiento, en los perfiles de diseño (las regiones malva), para obtener más información sobre la advertencia.

Paul-Sebastian Manole
fuente
0

Agregar mis ideas aquí ya que este hilo era la pregunta de "ir a" stackoverflow sobre el tema.

Mi problema estaba en una aplicación Material-UI (primeras etapas)

  • la ubicación del proveedor de tema personalizado fue la causa

cuando hice algunos cálculos forzando la representación de la página (un componente, "mostrar resultados", depende de lo que esté configurado en otros, "secciones de entrada").

Todo estuvo bien hasta que actualicé el "estado" que obliga al "componente de resultados" a volver a rendir. El problema principal aquí fue que tenía un tema material-ui ( https://material-ui.com/customization/theming/#a-note-on-performance ) en el mismo renderizador (App.js / return ..) como el "componente de resultados", SummaryAppBarPure

La solución fue elevar el ThemeProvider un nivel (Index.js) y ajustar el componente de la aplicación aquí, sin forzar al ThemeProvider a recalcular y dibujar / diseñar / refluir.

antes de

en App.js:

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

        <SummaryAppBarPure
//...

en index.js

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

después

en App.js:

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

        <SummaryAppBarPure
//...

en index.js

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

El reflujo forzado a menudo ocurre cuando tiene una función llamada varias veces antes del final de la ejecución.

Por ejemplo, puede tener el problema en un teléfono inteligente, pero no en un navegador clásico.

Sugiero usar a setTimeoutpara resolver el problema.

Esto no es muy importante, pero repito, el problema surge cuando se llama a una función varias veces, y no cuando la función tarda más de 50 ms. Creo que te equivocas en tus respuestas.

  1. Desactive las llamadas 1 por 1 y vuelva a cargar el código para ver si aún produce el error.
  2. Si una segunda secuencia de comandos causa el error, use una setTimeOutbasada en la duración de la violación.
Cherif
fuente
Esta no es una solución. Es una sugerencia mejor dejarla como comentario a la pregunta original.
Paul-Sebastian Manole
-6

Esto no es un error, solo un simple mensaje. Para ejecutar este mensaje, cambie
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">(ejemplo)
a
<!DOCTYPE html>(la fuente de Firefox lo espera)

El mensaje se mostró en Google Chrome 74 y Opera 60. Después de cambiarlo estaba claro, 0 detallado.
Un enfoque de solución

Gerdixx
fuente
55
Solo un consejo: su respuesta no tiene nada que ver con las preguntas. O arregla tu respuesta o quítala. La pregunta era "por qué la consola del navegador Chrome muestra una advertencia de violación". La respuesta es que es una función en los navegadores Chrome más nuevos, donde le avisa si la página web provoca reflujos excesivos del navegador al ejecutar JS. Consulte este recurso de Google para obtener más información.
Paul-Sebastian Manole