Por que os navegadores fazem throttling de timers em JavaScript
(nolanlawson.com)- Em JavaScript,
setTimeout(0)na prática muitas vezes não executa imediatamente e acaba tendo um atraso mínimo de 4 ms, que é uma limitação padrão do navegador para evitar abusos - Essas restrições existem para impedir que sites abusem indiscriminadamente de timers e causem consumo de bateria ou queda na interatividade; no modo de economia de bateria o limite pode subir para 16 ms, e em abas em segundo plano pode chegar a 1 segundo
- Para contornar as limitações do
setTimeout, desenvolvedores vêm usando várias APIs alternativas de timer, comosetImmediate,MessageChannel.postMessage,window.postMessageescheduler.postTask - Em benchmarks reais, Chrome e Firefox aplicam o clamp de 4 ms, mas
MessageChannelescheduler.postTaskfuncionam quase sem atraso, enquanto o Safari se destaca por restringirsetTimeoutde forma mais agressiva - No fundo, trata-se de um equilíbrio entre proteger a experiência do usuário e preservar a liberdade dos desenvolvedores; hoje a Scheduler API vem se consolidando como solução padronizada, mas, se houver abuso, novos casos de intervenção do navegador (Intervention) ainda podem surgir
O contexto por trás das restrições do setTimeout
- Mesmo com
setTimeout(0), por causa de abuso, a execução real muitas vezes só acontece pelo menos 4 ms depoisconst start = performance.now() setTimeout(() => { // executa após cerca de 4 ms console.log(performance.now() - start) }, 0) - O objetivo é impedir chamadas repetidas sem controle, reduzindo consumo de bateria e atrasos de renderização
- Alguns navegadores reforçam ainda mais essas limitações dependendo do ambiente
- Modo de bateria: 16 ms no Edge antigo
- Aba em segundo plano: no Chrome, o atraso pode chegar a 1 segundo
O surgimento de outras APIs de timer
setImmediate: suportado apenas no IE e no Edge antigo, hoje praticamente descontinuadoMessageChannel.postMessage: envia trabalho ao event loop por um canal separadowindow.postMessage: tem bom desempenho, mas há risco de conflito com outros scriptsscheduler.postTask: suportado por navegadores modernos e considerado a opção mais estável
Resultados de benchmark (MacBook Pro 2021, 101 repetições)
- Chrome 139:
setTimeout4.2ms,scheduler.postTask0ms - Firefox 142:
setTimeout4.72ms,scheduler.postTask0.01ms - Safari 18.4:
setTimeout26.73ms,MessageChannel0.52ms,window.postMessage0.05ms
Caso do fake-indexeddb
- O IndexedDB quer fazer commit automático da transação logo após o término das microtasks do event loop
- No Node.js,
setImmediateé ideal, mas no navegadorsetTimeouté ineficiente - Um trabalho que leva 300 ms no Chrome chegou a se estender até 4,8 segundos no navegador
- Como solução, passou-se a usar
scheduler.postTaskpor padrão e, por compatibilidade,MessageChannel/window.postMessagecomo fallback
A controvérsia sobre intervenção do navegador
- Um lado argumenta que timers precisam ser limitados para que os desenvolvedores sejam protegidos de si mesmos
- O outro lado defende que é preciso garantir liberdade para que os desenvolvedores possam medir e otimizar por conta própria
- No fim, seguindo o princípio de priorizar o usuário, os navegadores intervêm para evitar abusos
- A Scheduler API tenta conciliar as duas posições, oferecendo controle fino de tarefas aos desenvolvedores enquanto se alinha ao pipeline de renderização do navegador
Perspectivas futuras
postTaskepostMessagedevem continuar sem throttling por enquanto- Mas, se prioridades altas como
user-blockingforem usadas em excesso, novas intervenções podem voltar a acontecer - No longo prazo, talvez seja necessário até mesmo outra API alternativa, como uma hipotética
scheduler2
Ainda não há comentários.