Otimizar atraso de entrada

Descubra o que é o atraso de entrada e aprenda técnicas para reduzi-lo e tornar a interatividade mais rápida.

As interações na Web são complicadas, com todos os tipos de atividade ocorrendo no navegador para impulsioná-las. No entanto, todos eles têm em comum o fato de que sofrem algum atraso de entrada antes que os callbacks de eventos comecem a ser executados. Neste guia, você vai aprender o que é o atraso de entrada e o que pode ser feito para minimizá-lo, de modo que as interações do seu site sejam mais rápidas.

O que é latência na entrada?

O atraso de entrada é o período que começa quando o usuário interage pela primeira vez com uma página, como tocar na tela, clicar com o mouse ou pressionar uma tecla, até o momento em que os callbacks de evento para a interação começam a ser executados. Cada interação começa com um certo atraso de entrada.

Uma visualização simplificada do atraso de entrada. À esquerda, há uma arte de linha de um cursor de mouse com um efeito de estrela atrás, que significa o início de uma interação. À direita, há uma arte de linha de uma engrenagem, que indica quando os manipuladores de eventos de uma interação começam a ser executados. O espaço entre eles é indicado como o atraso de entrada com uma chave.
Os mecanismos por trás do atraso na entrada. Quando uma entrada é recebida pelo sistema operacional, ela precisa ser transmitida ao navegador antes do início da interação. Isso leva um certo tempo e pode ser aumentado pelo trabalho da linha de execução principal.

Parte do atraso de entrada é inevitável: sempre leva algum tempo para o sistema operacional reconhecer um evento de entrada e transmiti-lo ao navegador. No entanto, essa parte do atraso de entrada muitas vezes nem é perceptível, e há outras coisas que acontecem na própria página que podem causar atrasos de entrada longos o suficiente para causar problemas.

Como pensar no atraso de entrada

De modo geral, é recomendável manter cada parte de uma interação o mais curta possível para que seu site tenha a melhor chance de atender ao limiar "bom" da métrica Interaction to Next Paint (INP), independentemente do dispositivo do usuário. Manter o atraso de entrada em verificação é apenas uma parte do atendimento a esse limite.

Consequentemente, você vai querer ter o menor atraso de entrada possível para atender ao limite "bom" do INP. No entanto, não é possível eliminar totalmente os atrasos de entrada. Contanto que você evite o trabalho excessivo da linha de execução principal enquanto os usuários tentam interagir com a página, o atraso de entrada precisa ser baixo o suficiente para evitar problemas.

Como minimizar a latência de entrada

Como dito anteriormente, alguns atrasos de entrada são inevitáveis, mas, por outro lado, alguns podem ser evitados. Confira alguns pontos a serem considerados se você estiver com atrasos longos de entrada.

Evite timers recorrentes que iniciam um trabalho excessivo na linha de execução principal

Há duas funções de timer usadas com frequência no JavaScript que podem contribuir para o atraso de entrada: setTimeout e setInterval. A diferença entre os dois é que o setTimeout programa um callback para ser executado após um tempo especificado. setInterval, por outro lado, programa um callback para ser executado a cada n milissegundos indefinidamente ou até que o timer seja interrompido com clearInterval.

setTimeout não é problemático em si. Na verdade, ele pode ser útil para evitar tarefas longas. No entanto, isso depende de quando o tempo limite ocorre e se o usuário tenta interagir com a página quando o callback de tempo limite é executado.

Além disso, setTimeout pode ser executado em um loop ou de forma recursiva, atuando mais como setInterval, embora seja preferível não programar a próxima iteração até que a anterior seja concluída. Embora isso signifique que o loop vai ceder à linha de execução principal toda vez que setTimeout for chamado, é preciso ter cuidado para garantir que a callback não acabe fazendo um trabalho excessivo.

setInterval executa um callback em um intervalo e, portanto, tem muito mais probabilidade de atrapalhar as interações. Isso ocorre porque, ao contrário de uma única instância de uma chamada setTimeout, que é um callback único que pode atrapalhar uma interação do usuário, a natureza recorrente de setInterval torna muito mais provável que ela atrapalhará uma interação, aumentando o atraso de entrada da interação.

Captura de tela do perfilador de desempenho no Chrome DevTools mostrando o atraso de entrada. Uma tarefa acionada por uma função de timer ocorre pouco antes de um usuário iniciar uma interação de clique. No entanto, o timer estende o atraso de entrada, fazendo com que os callbacks de eventos da interação sejam executados mais tarde do que o normal.
Um timer registrado por uma chamada setInterval anterior que contribui para o atraso de entrada, conforme mostrado no painel de desempenho do Chrome DevTools. O atraso de entrada adicionado faz com que os callbacks de eventos para a interação sejam executados mais tarde do que o normal.

Se os timers estiverem ocorrendo no código próprio, você terá controle sobre eles. Avalie se você precisa deles ou faça o possível para reduzir o trabalho neles. No entanto, os timers em scripts de terceiros são uma história diferente. Muitas vezes, você não tem controle sobre o que um script de terceiros faz, e corrigir problemas de desempenho no código de terceiros geralmente envolve trabalhar com as partes interessadas para determinar se um determinado script de terceiros é necessário e, em caso afirmativo, entrar em contato com um fornecedor de script de terceiros para determinar o que pode ser feito para corrigir problemas de desempenho que eles podem causar no seu site.

Evitar tarefas longas

Uma maneira de reduzir os atrasos de entrada é evitar tarefas longas. Quando você tem um trabalho excessivo na linha de execução principal que bloqueia a linha de execução principal durante as interações, isso aumenta o atraso de entrada antes que as tarefas longas tenham a chance de terminar.

Uma visualização de quanto tempo as tarefas estendem o atraso de entrada. Na parte de cima, uma interação ocorre logo após a execução de uma única tarefa longa, causando um atraso de entrada significativo que faz com que os callbacks de eventos sejam executados muito mais tarde do que deveriam. Na parte de baixo, uma interação ocorre aproximadamente ao mesmo tempo, mas a tarefa longa é dividida em várias menores, permitindo que os callbacks de eventos da interação sejam executados muito mais cedo.
Uma visualização do que acontece com as interações quando as tarefas são muito longas e o navegador não consegue responder às interações com rapidez suficiente, em comparação com quando as tarefas mais longas são divididas em tarefas menores.

Além de minimizar a quantidade de trabalho que você faz em uma tarefa (e você sempre precisa se esforçar para fazer o mínimo possível de trabalho na linha de execução principal), é possível melhorar a capacidade de resposta à entrada do usuário dividindo tarefas longas.

Cuidado com a sobreposição de interações

Uma parte particularmente desafiadora da otimização da INP pode ser quando você tem interações que se sobrepõem. A sobreposição de interações significa que, depois de interagir com um elemento, você faz outra interação com a página antes que a interação inicial tenha a chance de renderizar o próximo frame.

Uma representação de quando as tarefas podem se sobrepor para produzir atrasos de entrada longos. Nesta representação, uma interação de clique se sobrepõe a uma interação de pressionamento de tecla para aumentar o atraso de entrada da interação de pressionamento de tecla.
Uma visualização de duas interações simultâneas no perfil de desempenho do Chrome DevTools. O trabalho de renderização na interação inicial do clique causa um atraso de entrada para a interação subsequente do teclado.

As fontes de sobreposição de interações podem ser tão simples quanto os usuários fazerem muitas interações em um curto período. Isso pode ocorrer quando os usuários digitam em campos de formulário, em que muitas interações do teclado podem ocorrer em um período muito curto. Se o trabalho em um evento principal for especialmente caro, como no caso comum de campos de preenchimento automático em que as solicitações de rede são feitas para um back-end, você terá algumas opções:

  • Considere desativar entradas para limitar a quantidade de vezes que um callback de evento é executado em um determinado período.
  • Use AbortController para cancelar solicitações fetch de saída para que a linha de execução principal não fique congestionada ao processar callbacks fetch. Observação: a propriedade signal de uma instância AbortController também pode ser usada para interromper eventos.

Outra fonte de aumento do atraso de entrada devido a interações sobrepostas pode ser animações caras. Especificamente, as animações em JavaScript podem disparar muitas chamadas requestAnimationFrame, o que pode atrapalhar as interações do usuário. Para contornar esse problema, use animações CSS sempre que possível para evitar a fila de frames de animação potencialmente caros. No entanto, evite animações não compostas para que as animações sejam executadas principalmente nas linhas de execução da GPU e do compositor, e não na linha de execução principal.

Conclusão

Embora os atrasos de entrada não representem a maioria do tempo que as interações levam para serem executadas, é importante entender que cada parte de uma interação consome um tempo que pode ser reduzido. Se você observar um delay de entrada longo, terá oportunidades para reduzi-lo. Evitar callbacks recorrentes de timer, dividir tarefas longas e estar ciente de possíveis sobreposições de interação podem ajudar a reduzir o atraso de entrada, levando a uma maior interatividade para os usuários do seu site.

Imagem principal do Unsplash, por Erik Mclean.