Otimizar atraso de entrada

Descubra o que é um atraso de entrada e aprenda técnicas para reduzi-lo e acelerar a interatividade.

As interações na Web são complicadas, e todos os tipos de atividade ocorrem no navegador para orientá-las. O que todos eles têm em comum, no entanto, é que incorrem em algum atraso de entrada antes que seus retornos de chamada de evento comecem a ser executados. Neste guia, você vai aprender o que é o tempo de entrada e como minimizar esse atraso para que as interações do seu site sejam mais rápidas.

O que é Input Delay?

Atraso na entrada é o período que começa na primeira interação do usuário com uma página (como tocar na tela, clicar com o mouse ou pressionar uma tecla) até o início da execução dos callbacks de evento da interação. Toda interação começa com um certo atraso de entrada.

Uma visualização simplificada do atraso de entrada. À esquerda, há um desenho de um cursor de mouse com um burst de estrela atrás dele, representando o início de uma interação. À direita está a linha de uma engrenagem, que significa quando os manipuladores de eventos de uma interação começam a ser executados. Esse espaço é indicado como o atraso de entrada com uma chave.
A mecânica por trás do atraso de entrada. Quando um sistema operacional recebe uma entrada, 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 existente.

Uma 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 tornar atrasos de entrada longos o suficiente para causar problemas.

Como considerar o atraso de entrada

De modo geral, convém manter cada parte de uma interação o mais curta possível para que seu site tenha a melhor chance de alcançar a métrica "boa" da métrica "Interação com a próxima pintura" (INP, na sigla em inglês) , independentemente do dispositivo do usuário. Verificar o atraso de entrada é apenas uma parte para atingir esse limite.

Consequentemente, procure o menor atraso de entrada possível para atender ao “bom” o limite mínimo. No entanto, você não pode esperar eliminá-los completamente. Desde que você evite o excesso de trabalho da linha de execução principal enquanto os usuários tentam interagir com sua página, o atraso de entrada deve ser baixo o suficiente para evitar problemas.

Como minimizar o atraso na entrada

Como dito antes, alguns atrasos de entrada são inevitáveis, mas alguns podem ser evitados. Aqui estão algumas coisas a serem consideradas se você estiver enfrentando atrasos de entrada longos.

Evite timers recorrentes que iniciem o trabalho excessivo da linha de execução principal

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

setTimeout não é um problema por si só. Na verdade, 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 loop ou de forma recursiva, em que atua mais como setInterval, embora, de preferência, não programe a próxima iteração até que a anterior seja concluída. Embora isso signifique que a repetição vai ser cedida à linha de execução principal sempre que setTimeout for chamado, tome cuidado para garantir que o callback não acabe fazendo trabalho excessivo.

O setInterval executa um callback em um intervalo e, portanto, tem muito mais chances 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 a interação do usuário, a natureza recorrente do setInterval aumenta muito a probabilidade de ele atrapalhar uma interação, aumentando assim o atraso de entrada da interação.

Uma captura de tela do gerador de perfis de desempenho no Chrome DevTools demonstrando 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 estende o atraso de entrada, fazendo com que os callbacks de evento da interação sejam executados mais tarde do que fariam de outra forma.
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 evento da interação sejam executados mais tarde do que poderiam.

Se os timers estiverem ocorrendo no código próprio, você terá controle sobre eles. Avalie se eles são necessários ou faça o melhor possível para reduzir ao máximo 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 a correção de 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, estabelecer contato com um fornecedor de script terceirizado para determinar o que pode ser feito para corrigir problemas de desempenho que eles podem causar no site.

Evitar tarefas longas

Uma forma de mitigar atrasos de entrada longos é evitar tarefas longas. Quando há excesso de trabalho 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 sejam concluídas.

Uma visualização de quanto tempo as tarefas aumentam o atraso na entrada. No topo, 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 inferior, uma interação ocorre praticamente ao mesmo tempo, mas a tarefa longa é dividida em várias menores por rendimento, permitindo que os callbacks de evento 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 rápido o suficiente a interações, em comparação com quando as tarefas mais longas são divididas em tarefas menores.

Além de minimizar a quantidade de trabalho realizado em uma tarefa (e você deve sempre 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.

Atenção às sobreposições de interação

Uma parte particularmente desafiadora da otimização do INP pode ser se você tiver interações que se sobrepõem. Isso significa que, depois de interagir com um elemento, você faz outra interação com a página antes que a interação inicial renderize o frame seguinte.

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 keydown para aumentar o atraso de entrada para a interação de keydown.
Uma visualização de duas interações simultâneas no Performance Profiler no DevTools do Chrome. O trabalho de renderização na interação de clique inicial causa um atraso de entrada para a interação subsequente com o teclado.

As fontes de sobreposição de interação podem ser tão simples quanto usuários realizando muitas interações em um curto período de tempo. Isso pode ocorrer quando os usuários digitam 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 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ê tem algumas opções:

  • Considere devolver 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 enviadas. Assim, a linha de execução principal não ficará congestionada processando callbacks fetch. Observação: a propriedade signal de uma instância AbortController também pode ser usada para cancelar eventos.

Outra fonte de aumento do atraso de entrada devido a interações sobrepostas podem ser animações caras. Mais especificamente, as animações em JavaScript podem disparar muitas chamadas requestAnimationFrame, o que pode atrapalhar as interações do usuário. Para contornar isso, use animações CSS sempre que possível para evitar enfileirar frames de animação potencialmente caros. Porém, se fizer isso, 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 possam não representar a maior parte do tempo que suas interações levam para serem executadas, é importante entender que cada parte de uma interação leva um tempo que pode ser reduzido. Se você estiver observando um atraso na entrada longo, terá oportunidades de reduzi-lo. Evitar callbacks recorrentes de timers, interromper tarefas longas e estar ciente de possíveis sobreposições de interação podem ajudar você a reduzir o atraso na entrada, levando a uma interatividade mais rápida para os usuários de seu site.

Imagem principal do Unsplash, de Erik Mclean.