Violação A tarefa JavaScript de execução demorada levou xx ms

331

Recentemente, recebi esse tipo de aviso e é a primeira vez que o recebo:

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

Estou trabalhando em um projeto de grupo e não tenho ideia de onde isso vem. Isso nunca aconteceu antes. De repente, apareceu quando alguém se envolveu no projeto. Como localizo qual arquivo / função causa esse aviso? Estive procurando a resposta, mas principalmente sobre a solução de como resolvê-lo. Não consigo resolver se não consigo encontrar a fonte do problema.

Nesse caso, o aviso aparece apenas no Chrome. Tentei usar o Edge, mas não recebi nenhum aviso semelhante e ainda não o testei no Firefox.

Eu até recebo o erro de jquery.min.js:

[Violation] Handler took 231ms of runtime (50ms allowed)            jquery.min.js:2
procador
fonte
Onde você vê esse aviso? Você não diz em que ambiente está trabalhando. Supondo algum navegador, mas qual etc?
Sami Kuhmonen
3
@SamiKuhmonen desculpe por isso, atualizei minha pergunta. eu usei o Chrome. Não encontrei nenhum erro semelhante no Edge.
procatmer
8
Eu só queria acrescentar que essa mensagem de aviso, apresentada no final de 2016, também pode aparecer devido a qualquer extensão que você tenha instalado no Chrome. É fácil verificar isso testando em modo privado.
Fer
1
Clicar no link do lado direito, indicando o script onde as violações acontecem, o levará ao local no código em que ocorre.
bluehipy
Estou usando o Ionic 4 (Angular 8), meu código estava funcionando bem, de repente esse tipo de violação começou a aparecer - não há dados aparecendo na minha lista agora?
Kapil Raghuwanshi 26/10/19

Respostas:

278

Atualização : o Chrome 58 ou mais oculta essas e outras mensagens de depuração por padrão. Para exibi-los, clique na seta ao lado de 'Informações' e selecione 'Detalhado'.

O Chrome 57 ativou 'ocultar violações' por padrão. Para ativá-los novamente, é necessário ativar os filtros e desmarcar a caixa "Ocultar violações".

de repente, aparece quando alguém mais envolvido no projeto

Acho que é mais provável que você tenha atualizado para o Chrome 56. Esse aviso é um recurso novo e maravilhoso. Na minha opinião, desative-o apenas se estiver desesperado e seu avaliador tirar marcas de você. Os problemas subjacentes existem nos outros navegadores, mas os navegadores simplesmente não estão dizendo que há um problema. O ticket do Chromium está aqui, mas não há realmente nenhuma discussão interessante sobre ele.

Essas mensagens são avisos em vez de erros, porque realmente não causam grandes problemas. Isso pode causar queda de quadros ou causar uma experiência menos suave.

No entanto, vale a pena investigar e corrigir para melhorar a qualidade do seu aplicativo. A maneira de fazer isso é prestando atenção em quais circunstâncias as mensagens aparecem e realizando testes de desempenho para diminuir onde o problema está ocorrendo. A maneira mais simples de iniciar o teste de desempenho é inserir um 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`);
}

Se você quiser se tornar mais avançado, também pode usar o criador de perfil do Chrome ou fazer uso de uma biblioteca de benchmarking como esta .

Depois de encontrar algum código que está demorando muito (50 ms é o limite do Chrome), você tem algumas opções:

  1. Corte algumas / todas as tarefas que podem ser desnecessárias
  2. Descubra como executar a mesma tarefa mais rapidamente
  3. Divida o código em várias etapas assíncronas

(1) e (2) pode ser difícil ou impossível, mas às vezes é realmente fácil e deve ser sua primeira tentativa. Se necessário, sempre deve ser possível (3). Para fazer isso, você usará algo como:

setTimeout(functionToRunVerySoonButNotNow);

ou

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

Você pode ler mais sobre a natureza assíncrona do JavaScript aqui .

voltrevo
fonte
16
Apenas uma sugestão, em vez de usar performance.now(), você poderia usar console.time( developer.mozilla.org/en-US/docs/Web/API/Console/time ) console.time('UniquetLabelName') ....code here.... console.timeEnd('UniqueLabelName')
denislexic
@denislexic Acho que sim. Não tenho certeza do valor que realmente agrega. Eu diria que aprender sobre a operação subjacente de obter o tempo atual e aproveitar isso é mais valioso.
voltrevo #
34
Ótima resposta, voltrevo! Minha pergunta é: se um código como esse é uma violação, do que exatamente ele está violando? Tem que haver algum tipo de padrão que o Google esteja aplicando, mas esse padrão está documentado publicamente em algum lugar?
trapalhão
1
@Bungler Dunno, eu gostaria de saber se há alguma orientação a que se refere também.
voltrevo 31/03
4
@Bungler Posso apenas supor que está dizendo que o código que está animando viola o fornecimento de pelo menos 60 quadros por segundo e, portanto, proporciona uma experiência ruim ao usuário. .
User895400
90

Estes são apenas avisos, como todos mencionados. No entanto, se você estiver interessado em resolvê-los (o que você deve), precisará identificar primeiro o que está causando o aviso. Não há um motivo para você receber um aviso de força de refluxo. Alguém criou uma lista para algumas opções possíveis. Você pode acompanhar a discussão para obter mais informações.
Aqui está a essência das possíveis razões:

O que força o layout / refluxo

Todas as propriedades ou métodos abaixo, quando solicitados / chamados em JavaScript, acionarão o navegador para calcular de forma síncrona o estilo e o layout *. Isso também é chamado de refusão ou thrashing de layout e é um gargalo de desempenho comum.

Elemento

Métricas de caixa
  • elem.offsetLeft, elem.offsetTop, elem.offsetWidth, elem.offsetHeight,elem.offsetParent
  • elem.clientLeft, elem.clientTop, elem.clientWidth,elem.clientHeight
  • elem.getClientRects(), elem.getBoundingClientRect()
Coisas de rolagem
  • elem.scrollBy(), elem.scrollTo()
  • elem.scrollIntoView(), elem.scrollIntoViewIfNeeded()
  • elem.scrollWidth, elem.scrollHeight
  • elem.scrollLeft, elem.scrollToptambém, configurando-os
Foco
  • elem.focus() pode acionar um layout forçado duplo ( origem )
Além disso…
  • elem.computedRole, elem.computedName
  • elem.innerText( fonte )

getComputedStyle

window.getComputedStyle()normalmente força o recálculo do estilo ( fonte )

window.getComputedStyle() forçará o layout também, se alguma das seguintes situações for verdadeira:

  1. O elemento está em uma árvore de sombra
  2. Existem consultas de mídia (relacionadas à viewport). Especificamente, um dos seguintes: ( fonte ) * min-width, min-height, max-width, max-height, width, height * aspect-ratio, min-aspect-ratio,max-aspect-ratio
    • device-pixel-ratio, resolution,orientation
  3. A propriedade solicitada é uma das seguintes: ( origem )
    • height, width * top, right, bottom, left * margin[ -top, -right, -bottom, -left, Ou abreviada ] somente se a margem é fixo. * padding[ -top, -right, -bottom, -left, Ou abreviada ] somente se o enchimento é fixo. * transform, transform-origin, perspective-origin * translate, rotate, scale * webkit-filter, backdrop-filter * motion-path, motion-offset, motion-rotation * x, y, rx,ry

janela

  • window.scrollX, window.scrollY
  • window.innerHeight, window.innerWidth
  • window.getMatchedCSSRules() apenas força o estilo

Formulários

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

Eventos do mouse

  • mouseEvt.layerX, mouseEvt.layerY, mouseEvt.offsetX, mouseEvt.offsetY ( Fonte de )

documento

  • doc.scrollingElement apenas força o estilo

Alcance

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

SVG

contenteditable

  • Muitas coisas, incluindo copiar uma imagem para a área de transferência ( fonte )

Veja mais aqui .

Além disso, aqui está o código-fonte do Chromium da edição original e uma discussão sobre uma API de desempenho para os avisos.


Editar: também há um artigo sobre como minimizar o refluxo do layout no PageSpeed ​​Insight do Google . Explica o refluxo do navegador:

Reflow é o nome do processo do navegador da web para recalcular as posições e geometrias dos elementos no documento, com a finalidade de renderizar novamente parte ou todo o documento. Como o reflow é uma operação de bloqueio do usuário no navegador, é útil para os desenvolvedores entenderem como melhorar o tempo de reflow e também os efeitos de várias propriedades do documento (profundidade do DOM, eficiência da regra CSS, diferentes tipos de alterações de estilo) no reflow Tempo. Às vezes, refletir um único elemento no documento pode exigir refletir seus elementos-pai e também quaisquer elementos que o sigam.

Além disso, explica como minimizá-lo:

  1. Reduza a profundidade DOM desnecessária. Alterações em um nível na árvore do DOM podem causar alterações em todos os níveis da árvore - até a raiz e até os filhos do nó modificado. Isso leva a mais tempo gasto realizando reflow.
  2. Minimize as regras CSS e remova regras CSS não utilizadas.
  3. Se você fizer alterações complexas na renderização, como animações, faça isso fora do fluxo. Use posição absoluta ou posição fixa para fazer isso.
  4. Evite seletores CSS complexos desnecessários - seletores descendentes em particular - que requerem mais energia da CPU para fazer a correspondência de seletores.
novato
fonte
1
Mais informações: o código-fonte do Chromium da edição original e uma discussão sobre uma API de desempenho para os avisos.
robocat
1
De acordo com o acima, a simples leitura de element.scrollTop aciona um reflow. Isso me parece um fenômeno contra-intuitivo. Eu posso entender por que definir element.scrollTop acionaria um reflow, mas simplesmente lendo seu valor? Alguém pode explicar melhor por que esse é o caso, se é que realmente é esse o caso?
David Edwards
29

Algumas idéias:

  • Remova metade do seu código (talvez por meio de um comentário).

    • O problema ainda está aí? Ótimo, você reduziu as possibilidades! Repetir.

    • O problema não está aí? Ok, olhe a metade que você comentou!

  • Você está usando algum sistema de controle de versão (por exemplo, Git)? Nesse caso, git checkoutalguns dos seus commits mais recentes. Quando o problema foi introduzido? Veja o commit para ver exatamente qual código foi alterado quando o problema chegou.

therobinkim
fonte
Obrigado pela sua resposta. Eu removi metade e até excluí meu arquivo .js principal do projeto. de alguma forma, o erro ainda ocorreu. é por isso que estou tão frustrado com isso. e sim, estou usando o git. Acabei de perceber esse erro hoje. houve muitos commits desde que este se tornou um projeto de grupo. pode fazer uma verificação profunda. obrigado novamente pelas idéias.
procatmer
O @procatmer usa a mesma estratégia para encontrar o commit do git. Por exemplo, se eu tivesse 10 confirmações (A, B, C, D, E, F, G, H, I, J) onde A era a mais antiga, git checkout Everificaria se o problema já existe. Se sim, continuarei a procurar o problema na primeira metade dos commits. Caso contrário, procuro o problema no segundo semestre.
Therobinkim
1
@procatmer Além disso, se você omitiu o .jsarquivo principal e o problema persistir ... pode ser uma biblioteca que você trouxe através de uma <script src="...">tag! Talvez algo que não valha a pena se preocupar (especialmente porque é apenas um aviso)?
therobinkim
1
Finalmente encontrei onde está o problema. usei sua segunda ideia para acompanhar as alterações. e sim, o problema vem de um .jsarquivo externo . aparentemente, isso importa. torna meu site lento bastante significativo. de qualquer forma, obrigado novamente por suas respostas e idéias.
procatmer
2
Você pode usar o git bisect para aplicar a pesquisa binária. Eu acho que é apenas para o propósito de encontrar bugs.
Pietrovismara #
12

Para identificar a origem do problema, execute seu aplicativo e registre-o na guia Desempenho do Chrome .

Lá você pode verificar várias funções que demoraram muito tempo para serem executadas. No meu caso, aquele que se correlacionou com os avisos no console era de um arquivo carregado pela extensão do AdBlock, mas isso poderia ser outra coisa no seu caso.

Verifique esses arquivos e tente identificar se esse é o código de alguma extensão ou o seu. (Se for seu, você encontrou a fonte do seu problema.)

Matt Leonowicz
fonte
Não, eu não tenho o AdBlock e ainda o consigo no console.
Nikola Stojaković
Tente analisá-lo com a guia Desempenho e procure a fonte das funções que executam muito tempo. Isso pode ser qualquer coisa, mas é uma maneira potencial de identificar a origem do problema.
Matt Leonowicz
6

Procure no console do Chrome na guia Rede e encontre os scripts que demoram mais para carregar.

No meu caso, havia um conjunto de scripts Angular add que eu incluíra, mas ainda não usei no aplicativo:

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

Esses foram os únicos arquivos JavaScript que demoraram mais para carregar do que o tempo especificado pelo erro "Tarefa de longa execução".

Todos esses arquivos são executados em meus outros sites sem erros gerados, mas eu estava recebendo o erro "Tarefa de execução longa" em um novo aplicativo da Web que mal possuía funcionalidade. O erro parou imediatamente após a remoção.

Meu melhor palpite é que esses complementos angulares estavam procurando recursivamente em seções cada vez mais profundas do DOM suas tags de início - não encontrando nenhuma, eles precisavam atravessar todo o DOM antes de sair, o que demorou mais do que o Chrome espera - portanto, o aviso.

Jordan Reddick
fonte
6

Encontrei a raiz desta mensagem no meu código, que pesquisou e ocultou ou mostrou nós (offline). Este foi o meu 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');
});

A guia desempenho (criador de perfil) mostra o evento com aproximadamente 60 ms: Refluxo de recálculo de layout de perfil de desempenho de cromo

Agora:

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

A guia desempenho (criador de perfil) agora mostra o evento com aproximadamente 1 ms: Perfilador de cromo escuro

E sinto que a pesquisa funciona mais rápido agora (229 nós).

Vitaly Zdanevich
fonte
3
Em resumo, ao receber a violação, você conseguiu otimizar seu código e ele tem um desempenho melhor agora.
Usuário que não é usuário
3

Encontrei uma solução no código fonte do Apache Cordova. Eles implementam assim:

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

Implementação simples, mas de maneira inteligente.

No Android 4.4, use Promise. Para navegadores mais antigos, usesetTimeout()


Uso:

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

Depois de inserir esse código de truque, todas as mensagens de aviso desaparecem.

wf9a5m75
fonte
2

Se você estiver usando o Chrome Canary (ou Beta), basta marcar a opção "Ocultar violações".

Caixa de seleção Ocultar violações no console do Chrome 56

zhaoming
fonte
1

Este é um erro de violação do Google Chrome que mostra quando o Verbosenível de log está ativado.

Exemplo de mensagem de erro:

captura de tela do aviso

Explicação:

Reflow é o nome do processo do navegador da web para recalcular as posições e geometrias dos elementos no documento, com a finalidade de renderizar novamente parte ou todo o documento. Como o reflow é uma operação de bloqueio do usuário no navegador, é útil para os desenvolvedores entenderem como melhorar o tempo de reflow e também os efeitos de várias propriedades do documento (profundidade do DOM, eficiência da regra CSS, diferentes tipos de alterações de estilo) no reflow Tempo. Às vezes, refletir um único elemento no documento pode exigir refletir seus elementos-pai e também quaisquer elementos que o sigam.

Artigo original: minimizando o refluxo do navegador por Lindsey Simon, desenvolvedor do UX, publicado em developers.google.com.

E este é o link que o Google Chrome fornece no perfil de desempenho, nos perfis de layout (regiões malva), para mais informações sobre o aviso.

Paul-Sebastian Manole
fonte
0

Adicionando meus insights aqui como esse encadeamento foi a pergunta "ir para" stackoverflow sobre o tópico.

Meu problema estava em um aplicativo de interface do usuário de material (estágios iniciais)

  • A colocação do provedor de temas personalizado foi a causa

quando fiz alguns cálculos forçando a renderização da página (um componente, "exibir resultados", depende do que está definido em outros, "seções de entrada").

Tudo estava bem até eu atualizar o "estado" que força o "componente de resultados" a ser renderizado novamente. O principal problema aqui foi que eu tinha um tema de interface do usuário material ( https://material-ui.com/customization/theming/#a-note-on-performance ) no mesmo renderizador (App.js / return ..) como o "componente de resultados", SummaryAppBarPure

A solução foi elevar o ThemeProvider um nível acima (Index.js) e agrupar o componente App aqui, não forçando o ThemeProvider a recalcular e desenhar / layout / reflow.

antes

no App.js:

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

        <SummaryAppBarPure
//...

no index.js

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

depois de

no App.js:

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

        <SummaryAppBarPure
//...

no index.js

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

O refluxo forçado geralmente acontece quando você tem uma função chamada várias vezes antes do final da execução.

Por exemplo, você pode ter o problema em um smartphone, mas não em um navegador clássico.

Sugiro usar um setTimeoutpara resolver o problema.

Isso não é muito importante, mas repito, o problema surge quando você chama uma função várias vezes, e não quando a função leva mais de 50 ms. Eu acho que você está enganado em suas respostas.

  1. Desative as chamadas 1 por 1 e recarregue o código para ver se ele ainda produz o erro.
  2. Se um segundo script causar o erro, use um com setTimeOutbase na duração da violação.
Cherif
fonte
Esta não é uma solução. É melhor deixar uma sugestão como um comentário à pergunta original.
Paul-Sebastian Manole
-6

Este não é um erro, apenas uma mensagem simples. Para executar esta mensagem, mude
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">(exemplo)
para
<!DOCTYPE html>(a fonte do Firefox espera isso).

A mensagem foi mostrada no Google Chrome 74 e Opera 60. Depois de mudar, ficou claro, 0 detalhado.
Uma abordagem de solução

Gerdixx
fonte
5
Apenas alguns conselhos: sua resposta não tem nada a ver com as perguntas. Corrija sua resposta ou remova-a. A pergunta era "por que o console do navegador Chrome mostra um aviso de violação". A resposta é que ele é um recurso dos navegadores Chrome mais recentes, onde é alertado se a página da Web causa um excesso de navegador durante a execução do JS. Consulte este recurso do Google para mais informações.
Paul-Sebastian Manole