HeadlinesBriefing favicon HeadlinesBriefing.com

Performance Thread Principal Navegador

Hacker News •
×

O que vem à mente quando você ouve "otimização de frontend"? Para a maioria de nós, são coisas como reduzir requisições de rede, diminuir o bundle, ou fazer bom uso do cache. Além disso, talvez reduzir re-renders ou ajustar quando os recursos são carregados. O thread principal geralmente não vem à tona, e há uma razão para isso: na maioria das telas, ele nunca se torna um problema.

Mas em telas com muita interação, onde dados entram ao vivo e rolagem, animação e entrada se entrelaçam, a situação muda. Por mais que você economize em rede e tamanho do bundle, a tela congela no momento em que o thread principal é bloqueado. Você provavelmente já encontrou um site onde a rolagem gagueja de vez em quando, um botão responde com um leve atraso, ou as letras que você digita em uma caixa de busca aparecem meio tempo atrasadas.

Não é ruim o suficiente para ser irritante, mas incomoda de forma sutil. Esse tipo de "jank" é o que um thread principal bloqueado parece. Quando nos deparamos com jank como este como desenvolvedores, a reação usual é se perguntar "meu código é lento?" e começar a dissecar algoritmos ou procurar computação desperdiçada.

Na maioria dos casos, porém, a velocidade do código não é o problema. O código não é lento. Acontece apenas que é o código que está segurando o thread principal.

O navegador tem vários threads, mas quase tudo que podemos tocar a partir do código está concentrado no thread principal. Cálculo, renderização, manipulação de eventos, manipulação de resposta de rede, e os internos do seu framework são todos processados lá. Um recurso, uma montanha de trabalho.

O thread principal do navegador é caro. A maior parte do tempo ele não causa problemas, mas uma vez que você tenta fazer algo ambicioso, lidar com o thread principal se torna a parte importante. Este artigo é sobre como lidar com esse recurso caro.

Vamos começar com o que o thread principal realmente faz. Seu trabalho se divide em duas grandes categorias. A primeira é executar JavaScript.

O código que escrevemos, junto com manipuladores de eventos, timers, callbacks de resposta de rede, e os internos do framework, tudo roda aqui. Essas tarefas executam na ordem em que entram na fila, sempre que há um intervalo, sem relação com o ciclo de atualização da tela. A segunda é desenhar a tela.

Quando o DOM ou estilos mudam e a tela precisa ser atualizada, o navegador passa aproximadamente por estes passos, em ordem, para produzir um frame. Executar callbacks requestAnimationFrame — JavaScript registrado para rodar logo antes do frame ser desenhado Cálculo de estilo — calcular os valores CSS finais para cada elemento Layout — calcular a posição e tamanho de cada elemento (também chamado de reflow) Paint — gerar comandos de paint descrevendo o que desenhar em quais cores Se nada mudou, esses passos são pulados completamente, então eles não necessariamente rodam a cada frame. Apenas o passo final de composição, que pega a saída produzida e a monta na tela, é passado para o thread compositor.

Em outras palavras, a maior parte da primeira metade do pipeline que desenha a tela é responsabilidade do thread principal. O pipeline de renderização para atualizar a tela Para que a tela pareça suave, frames têm que ser desenhados na taxa de atualização do display. No display 60Hz mais comum, isso significa 60 frames por segundo, ou cerca de 16,6 milissegundos por frame.

E você não consegue usar tudo. Uma vez subtraído o custo de processamento próprio do navegador, o orçamento prático é geralmente considerado em torno de 10 milissegundos, e em um dispositivo 120Hz o orçamento em si é cortado pela metade. O problema é que os dois tipos de trabalho acima ficam em uma única fila no mesmo thread.

JavaScript foi projetado em torno de um modelo de loop de eventos de thread único. O thread principal processa uma tarefa por vez, e enquanto essa tarefa está rodando, nada mais pode acontecer. Se uma função JavaScript roda por 200 milissegundos, então por aqueles 200 milissegundos o navegador não pode repintar a tela nem receber um clique do usuário.

Contra um orçamento de frame de cerca de 10 milissegundos, isso é uma quantidade fatal de tempo. Uma tarefa que ru...