Otimizar atraso de entrada

Descubra o que é o atraso de entrada e aprenda técnicas para reduzir esse problema e melhorar a interatividade.

As interações na Web são complicadas, com todos os tipos de atividades ocorrendo no navegador para impulsioná-las. No entanto, todas elas têm em comum o fato de que incorrem em 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 fazer para minimizar esse problema e acelerar as interações do seu site.

O que é latência na entrada?

O atraso na entrada é o período de tempo que começa quando o usuário interage pela primeira vez com uma página (por exemplo, tocar em uma tela, clicar com um mouse ou pressionar uma tecla) e vai até o momento em que os callbacks de eventos da interação começam a ser executados. Toda interação começa com um certo atraso de entrada.

Uma visualização simplificada do atraso de entrada. À esquerda, há uma arte linear de um cursor do mouse com uma explosão de estrelas atrás, significando o início de uma interação. À direita, há uma arte linear de uma engrenagem, que significa 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.
A mecânica por trás da latência 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, que 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 geralmente não é perceptível, e há outras coisas que acontecem na própria página que podem fazer com que os atrasos de entrada sejam longos o suficiente para causar problemas.

Como pensar sobre o atraso de entrada

Em 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), independente do dispositivo do usuário. Manter o atraso de entrada sob controle é apenas uma parte do atendimento a esse limite.

Portanto, procure o menor atraso de entrada possível para atender ao limite "bom" do INP. No entanto, não é possível eliminar completamente os atrasos de entrada. Desde que você evite o trabalho excessivo da linha de execução principal enquanto os usuários tentam interagir com sua página, o atraso de entrada será baixo o suficiente para evitar problemas.

Como minimizar o atraso de entrada

Como dito anteriormente, algum atraso na entrada é inevitável, mas, por outro lado, algum atraso pode ser evitado. Confira algumas coisas a considerar se você estiver com dificuldades devido a longos atrasos de entrada.

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

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

setTimeout não é problemático por si só. 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, agindo mais como setInterval, embora seja preferível não programar a próxima iteração até que a anterior seja concluída. Isso significa que o loop vai gerar a linha de execução principal sempre que setTimeout for chamado. No entanto, tome cuidado para garantir que o callback não acabe fazendo trabalho excessivo.

setInterval executa um callback em um intervalo e, portanto, é muito mais provável que atrapalhe as interações. Isso acontece 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 do setInterval aumenta muito a probabilidade de que ele vai atrapalhar uma interação, aumentando assim o atraso de entrada dela.

Uma captura de tela do criador de perfil de performance no Chrome DevTools mostrando o atraso de entrada. Uma tarefa disparada por uma função de timer ocorre pouco antes de um usuário iniciar uma interação de clique. No entanto, o timer aumenta o atraso de entrada, fazendo com que os callbacks de eventos da interação sejam executados mais tarde do que seriam.
Um timer registrado por uma chamada setInterval anterior que contribui para o atraso de entrada, conforme mostrado no painel de performance do Chrome DevTools. O atraso de entrada adicionado faz com que os callbacks de eventos da interação sejam executados mais tarde do que poderiam.

Se os timers estiverem ocorrendo em 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 diferentes. Muitas vezes, você não tem controle sobre o que um script de terceiros faz. Para corrigir problemas de desempenho no código de terceiros, é preciso 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 ele possa causar no seu site.

Evitar tarefas longas

Uma maneira de reduzir os longos atrasos de entrada é evitar tarefas longas. Quando você tem trabalho excessivo na linha de execução principal que a bloqueia 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 significativo na entrada 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 por yield, permitindo que os callbacks de eventos da interação sejam executados muito antes.
Uma visualização do que acontece com as interações quando as tarefas são muito longas e o navegador não consegue responder 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 em uma tarefa (e você deve sempre tentar fazer o mínimo possível na linha de execução principal), é possível melhorar a capacidade de resposta à entrada do usuário dividindo tarefas longas.

Evite a sobreposição de interações

Uma parte particularmente desafiadora da otimização do INP pode ser quando há 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 longos atrasos de entrada. 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 criador de perfis de desempenho no DevTools do Chrome. O trabalho de renderização na interação de clique inicial causa um atraso de entrada para a interação de teclado subsequente.

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

  • Considere eliminar repetições de entradas para limitar a quantidade de vezes que um retorno de chamada de evento é executado em um determinado período.
  • Use AbortController para cancelar solicitações fetch de saída e evitar que a linha de execução principal 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 no atraso de entrada devido a interações sobrepostas são animações caras. Em particular, as animações em JavaScript podem acionar muitas chamadas requestAnimationFrame, o que pode atrapalhar as interações do usuário. Para evitar isso, use animações CSS sempre que possível para evitar o enfileiramento de frames de animação potencialmente caros. No entanto, se você fizer isso, evite animações não combinadas para que elas sejam executadas principalmente na GPU e nas linhas de execução do compositor, e não na linha de execução principal.

Conclusão

Embora os atrasos de entrada não representem a maior parte do tempo que suas interações levam para ser executadas, é importante entender que cada parte de uma interação leva um tempo que pode ser reduzido. Se você estiver observando um atraso longo na entrada, há oportunidades para reduzir esse tempo. Evitar callbacks de timer recorrentes, dividir tarefas longas e estar ciente de uma possível sobreposição de interação pode ajudar a reduzir o atraso de entrada, resultando em uma interatividade mais rápida para os usuários do seu site.

Imagem principal do Unsplash, de Erik Mclean.