Por que o Cronômetro do Navegador Atrasa, e Como Ele Evita Isso
Deixe um cronômetro rodando numa aba, vá fazer outra coisa por dez minutos e volte. Ou o número está certo, ou ele está bem atrás do que deveria. A diferença entre os dois casos não está no navegador, está em como o cronômetro foi escrito.
O que a especificação promete
Vale começar pelo que o navegador se compromete a fazer quando alguém pede um disparo a cada tanto de tempo. O HTML Standard da WHATWG é direto sobre os limites:
This API does not guarantee that timers will run exactly on schedule. Delays due to CPU load, other tasks, etc, are to be expected.
E vai além de admitir atraso: ele autoriza o navegador a atrasar de propósito.
Optionally, wait a further implementation-defined length of time. This is intended to allow user agents to pad timeouts as needed to optimize the power usage of the device.
Há ainda um piso explícito para chamadas encadeadas. Depois do quinto nível de aninhamento, um intervalo pedido abaixo de quatro milissegundos é elevado para quatro:
Timers can be nested; after five such nested timers, however, the interval is forced to be at least four milliseconds.
Ou seja, pedir um disparo a cada 10 milissegundos é um pedido, não um contrato. O navegador entrega quando puder, e tem permissão para entregar menos vezes do que você pediu.
Duas formas de escrever a mesma tela
Somando disparos. A cada disparo, some o intervalo pedido a um acumulador. É a versão intuitiva: pedi 10 milissegundos, então cada disparo vale 10 milissegundos. O problema é que ela trata a promessa como se fosse o fato, e todo disparo que não aconteceu vira tempo que sumiu para sempre.
Lendo o relógio. Guarde o instante da largada e, a cada disparo, mostre a diferença entre o instante atual e aquele. O disparo deixa de ser a fonte do tempo e passa a ser apenas o aviso de que é hora de redesenhar a tela. Se o navegador atrasar, engolir disparos ou parar de emiti-los enquanto a aba está escondida, o próximo disparo já mostra o valor certo, porque a conta nunca dependeu deles.
O tamanho do erro
Para medir a diferença, rodei um teste com intervalo de 10 milissegundos por cerca de cinco segundos, com uma carga sintética bloqueando o laço de eventos por 45 milissegundos a cada 60. É uma carga deliberadamente pesada, escolhida para tornar o efeito visível numa execução curta.
| Medida | Valor |
|---|---|
| Intervalo pedido | 10 ms |
| Tempo real decorrido | 5,04 s |
| Disparos esperados se o intervalo fosse exato | 504 |
| Disparos efetivamente recebidos | 167 |
| Cronômetro que soma disparos | 1,67 s |
| Cronômetro que lê o relógio | 5,04 s |
Dois terços dos disparos simplesmente não aconteceram. O cronômetro que confiou neles marcou um terço do tempo real, e não havia nada de errado com o navegador: ele estava fazendo exatamente o que a especificação permite.
Os seus números vão ser outros, porque dependem da máquina, do navegador e do que mais estiver rodando. O que não muda é a direção do erro. Um cronômetro que soma disparos nunca adianta, ele só atrasa, e o atraso é cumulativo.
Por que a aba em segundo plano é o pior caso
Enquanto a página está visível, o navegador tem motivo para manter os disparos em dia, porque alguém está olhando. Quando a aba vai para trás, a permissão de “aguardar um tempo adicional” para economizar energia passa a valer com força total, e a frequência dos disparos despenca.
Aí está o teste caseiro que separa os dois tipos de cronômetro em um minuto:
- Inicie a contagem e anote a hora no relógio do celular.
- Troque de aba, ou minimize a janela, por um ou dois minutos.
- Volte e compare o cronômetro com o tempo que passou no celular.
Se os dois baterem, aquele cronômetro lê o relógio do sistema. Se ele voltou atrasado, ele estava somando disparos. É a mesma razão pela qual o cronômetro do kwyper continua correndo com a aba escondida: a tela para de ser redesenhada, o tempo não para de ser contado.
O que continua impreciso mesmo fazendo certo
Ler o relógio elimina a deriva acumulada, e só ela. Três fontes de erro permanecem, todas pequenas e nenhuma delas eliminável no navegador.
A primeira é a latência entre você decidir apertar o botão e o clique virar um evento processado. A segunda é o agendamento: mesmo o disparo que chega pontual chega depois do instante em que deveria. A terceira é a taxa de atualização da tela, que faz o número exibido ser sempre uma fotografia levemente atrasada do valor interno.
Somadas, essas três costumam ficar na casa das dezenas de milissegundos, o que é irrelevante para treino, estudo, cozinha ou prova, e é muito acima do aceitável para cronometragem oficial. Por isso os centésimos no visor devem ser lidos como resolução de exibição, não como garantia de exatidão naquela casa decimal.
Fontes e metodologia
As regras de agendamento de temporizadores foram lidas diretamente no capítulo de timers do HTML Standard da WHATWG, que é um padrão vivo e pode mudar sem aviso; a consulta foi feita na data indicada. A medição de deriva não é estimativa: rodei o teste no Node.js com uma carga sintética declarada no próprio texto, bloqueando o laço de eventos por 45 ms a cada 60 ms, e os números da tabela são os que saíram dessa execução. Uma execução sua vai dar números diferentes, porque o resultado depende da carga da máquina, do navegador e do sistema; o que se repete é a direção do erro, não a magnitude. O teste ilustra o mecanismo do atraso; ele não mede o desempenho de nenhum navegador específico.
- HTML Standard — Timers (padrão vivo) WHATWG A regra de que temporizadores aninhados passam a ter intervalo mínimo de quatro milissegundos depois do quinto nível; a permissão explícita para o navegador aguardar um tempo adicional definido pela implementação, com o objetivo declarado de otimizar o consumo de energia do dispositivo; e a ressalva de que a API não garante execução no horário exato, sendo esperados atrasos por carga de CPU e outras tarefas. Consultado em
Perguntas frequentes
O cronômetro continua contando com a aba em segundo plano?
Continua, se ele for construído a partir do relógio do sistema. Nesse caso o tempo decorrido é sempre a diferença entre o instante atual e o instante da largada, e a aba em segundo plano só deixa de redesenhar a tela. Um cronômetro que soma disparos, por outro lado, congela junto com os disparos.
Por que o navegador atrasa os temporizadores?
Porque a especificação permite. O HTML Standard diz que a API não garante execução no horário exato e que atrasos por carga de CPU e outras tarefas são esperados, além de autorizar o navegador a aguardar um tempo adicional para economizar energia do dispositivo.
O que é o limite de quatro milissegundos?
É uma regra do padrão para temporizadores aninhados: passando do quinto nível de aninhamento, um intervalo pedido abaixo de quatro milissegundos é elevado para quatro. Isso põe um piso na frequência máxima de disparos, independentemente do valor solicitado.
Dá para confiar nos centésimos de segundo?
Como leitura, sim, desde que venham do relógio do sistema. Como garantia de exatidão, não: entre o instante do seu clique e o registro do tempo existem latência de entrada, agendamento do navegador e taxa de atualização da tela. Os centésimos são resolução de exibição, não precisão de medição.
Como testo se o cronômetro que uso está correto?
Inicie a contagem, troque de aba por um ou dois minutos e volte. Compare com o relógio do celular. Se os dois baterem, o cronômetro lê o relógio do sistema; se ele voltou atrasado, ele estava somando disparos e perdeu os que não aconteceram.
Cronômetro de navegador serve para competição oficial?
Não. A soma de latência de clique, agendamento e atualização de tela é pequena para treino, cozinha e estudo, mas está muito acima do exigido por cronometragem homologada. Para uso comum ela é irrelevante; para resultado oficial, o equipamento é outro.
Trocar setInterval por setTimeout resolve o atraso?
Não resolve, porque as duas APIs estão sujeitas às mesmas permissões de atraso. O que resolve é não usar o temporizador como fonte de tempo: ele serve para decidir quando redesenhar a tela, enquanto o valor exibido vem sempre da diferença entre dois instantes do relógio.
Recarregar a página zera o cronômetro?
Zera, porque o instante da largada vive na memória da página e nada é salvo automaticamente. Se precisar guardar os tempos, copie ou baixe as voltas antes de sair da página.