Otimizar a execução do JavaScript

O JavaScript muitas vezes aciona mudanças visuais. Às vezes, diretamente por manipulação de estilo e, às vezes, por cálculos que resultam em mudanças visuais, como buscar ou classificar dados. JavaScript de longa duração ou no momento errado é uma causa comum de problemas de desempenho. Você deve buscar minimizar seu impacto ao máximo.

O JavaScript muitas vezes aciona mudanças visuais. Às vezes, isso acontece diretamente por manipulações de estilo e, às vezes, por cálculos que resultam em mudanças visuais, como pesquisar ou classificar dados. JavaScript de longa duração ou no momento errado é uma causa comum de problemas de desempenho. Você deve buscar minimizar seu impacto ao máximo.

A criação de perfis de desempenho do JavaScript pode ser algo complexo porque o JavaScript que você escreve não se parece em nada com o código realmente executado. Navegadores modernos usam compiladores JIT e todas as formas de otimizações e truques para tentar oferecer a execução mais rápida possível. Isso muda consideravelmente a dinâmica do código.

No entanto, há certamente algumas coisas que podem ser feitas para melhorar a execução do JavaScript nos apps.

Resumo

  • Evite usar setTimeout ou setInterval para mudanças visuais. Sempre use requestAnimationFrame.
  • Remova o JavaScript de longa duração do encadeamento principal para os Web Workers.
  • Use microtarefas para realizar mudanças no DOM ao longo de vários frames.
  • Use a Timeline e o JavaScript Profiler do Chrome DevTools para avaliar o impacto do JavaScript.

Use requestAnimationFrame para mudanças visuais

Quando mudanças visuais ocorrem na tela, recomendamos fazer o trabalho no momento certo para o navegador, ou seja, no início do frame. A única forma de garantir que o JavaScript seja executado no início de um frame é usando 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);

Frameworks ou exemplos podem usar setTimeout ou setInterval para fazer mudanças visuais, como animações. No entanto, o problema dessa abordagem é que o callback será executado em algum ponto no frame, possivelmente no final. Isso pode causar a perda de um frame e, consequentemente, instabilidade.

setTimeout faz o navegador perder um frame.

Na verdade, o jQuery costumava usar setTimeout para o comportamento animate. Ele foi alterado para usar requestAnimationFrame na versão 3. Se você estiver usando uma versão mais antiga do jQuery, poderá corrigi-lo para usar requestAnimationFrame, o que é altamente recomendado.

Reduza a complexidade ou use Web Workers

O JavaScript funciona no encadeamento principal do navegador, juntamente com cálculos de estilo, layout e, em muitos casos, pintura. Se a execução do JavaScript demorar muito tempo, ele vai bloquear essas outras tarefas, causando possivelmente perda de frames.

Seja tático quanto ao momento e ao tempo de execução do JavaScript. Por exemplo, em uma animação como rolagem, mantenha o JavaScript próximo de 3 a 4 ms. Um tempo maior que esse pode ser demais. Em períodos de inatividade, há mais flexibilidade quanto ao tempo decorrido.

Em muitos casos, você pode delegar trabalho computacional puro para Web Workers, se, por exemplo, não for necessário acessar o DOM. Manipular ou percorrer dados, como na classificação ou pesquisa, são com frequência atividades adequadas para esse modelo, bem como carregamento e geração de modelo.

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

Nem todo trabalho é adequado a esse modelo: os Web Workers não têm acesso ao DOM. Quando for necessário executar seu trabalho no encadeamento principal, considere uma abordagem em lotes, onde a tarefa maior é segmentada em microtarefas, cada uma delas com duração de poucos milissegundos e executada dentro de gerenciadores requestAnimationFrame em cada frame.

Essa abordagem afeta a interface e a experiência do usuário. Será necessário garantir que o usuário saiba que uma tarefa está sendo processada, usando um indicador de andamento ou de atividade. Em qualquer caso, essa abordagem vai manter a linha de execução principal do app livre, ajudando a permanecer responsivo para as interações do usuário.

Conheça a "taxa de frames" do JavaScript

Ao avaliar um framework, biblioteca ou seu próprio código, é importante estimar o custo da execução do código JavaScript quadro a quadro. Isso é especialmente importante em trabalhos de animação em que o desempenho é crítico, como transição ou rolagem.

O painel Performance do Chrome DevTools é a melhor maneira de medir o custo do JavaScript. Normalmente, você recebe registros de baixo nível como este:

Uma gravação de desempenho no Chrome DevTools

A seção Main mostra um diagrama de chamas das chamadas JavaScript para que você possa analisar exatamente quais funções foram chamadas e quanto tempo cada uma delas levou.

Com essas informações, você pode avaliar o impacto do desempenho do JavaScript no aplicativo e começar a localizar e corrigir todos os pontos problemáticos em que as funções estão demorando muito para ser executadas. Como mencionado anteriormente, remova o JavaScript de longa execução ou, se isso não for possível, transfira-o para um Web Worker, liberando o encadeamento principal para continuar com outras tarefas.

Consulte Começar a analisar o desempenho de execução para saber como usar o painel "Performance".

Evite a micro-otimização do JavaScript

Pode ser legal saber que o navegador pode executar uma versão de uma coisa 100 vezes mais rápido que outra, como solicitar o offsetTop de um elemento é mais rápido que o cálculo getBoundingClientRect(), mas quase sempre é verdade que você só vai chamar funções como essas um pequeno número de vezes por frame. Portanto, é normal desperdiçar esforço para se concentrar nesse aspecto do desempenho do JavaScript. Normalmente, a economia será apenas de algumas frações de milissegundos.

Se você estiver criando um jogo ou um aplicativo com alto custo computacional, essa regra não se aplicará, pois provavelmente haverá muita computação em um único quadro e, nesse caso, tudo ajuda.

Resumindo, você deve ser muito cauteloso com micro-otimizações porque elas geralmente não são adequadas ao tipo de aplicativo que você está criando.