requestAnimationFrame vs setTimeout: Quando Usar Cada Um

Ao construir animações suaves ou agendar tarefas em JavaScript, escolher entre requestAnimationFrame
e setTimeout
pode impactar significativamente o desempenho da sua aplicação. Embora ambos os métodos agendem a execução de funções, eles servem a propósitos fundamentalmente diferentes e operam com mecanismos de temporização distintos.
Pontos-Chave
requestAnimationFrame
sincroniza com a taxa de atualização da tela do navegador para atualizações visuais suavessetTimeout
fornece temporização de propósito geral para tarefas não-visuais e operações em segundo plano- Usar o método errado pode causar problemas de desempenho, consumo excessivo de bateria e má experiência do usuário
requestAnimationFrame
pausa automaticamente em abas inativas, enquantosetTimeout
continua executando
Entendendo as Diferenças Fundamentais
setTimeout: O Temporizador de Propósito Geral
setTimeout
executa uma função após um atraso especificado em milissegundos. É um temporizador simples e previsível que funciona independentemente do ciclo de renderização do navegador.
// Execute após 1 segundo
setTimeout(() => {
console.log('Um segundo se passou');
}, 1000);
// Com parâmetros
setTimeout((message) => {
console.log(message);
}, 2000, 'Olá após 2 segundos');
A característica principal do setTimeout
é sua execução baseada na fila de tarefas. Quando o atraso expira, o callback se junta à fila de tarefas do event loop do JavaScript, competindo com outras tarefas pelo tempo de execução.
requestAnimationFrame: O Especialista em Animações
requestAnimationFrame
(rAF) sincroniza com o ciclo de repaint do navegador, tipicamente executando a 60 quadros por segundo na maioria das telas. É especificamente projetado para atualizações visuais.
function animate(timestamp) {
// Atualiza animação baseada no timestamp
const element = document.getElementById('animated-element');
element.style.transform = `translateX(${timestamp / 10}px)`;
// Continua animação
if (timestamp < 5000) {
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
Considerações de Desempenho e Temporização
Integração com o Pipeline de Renderização do Navegador
A vantagem fundamental do requestAnimationFrame
reside em sua integração com o pipeline de renderização do navegador. Enquanto setTimeout
dispara sempre que seu atraso expira, os callbacks do requestAnimationFrame
executam logo antes do navegador calcular o layout e pintar pixels na tela.
Esta sincronização elimina problemas comuns de animação:
- Screen tearing: Artefatos visuais de atualizações no meio do quadro
- Jank: Temporização irregular de quadros causando movimento entrecortado
- Renderizações desperdiçadas: Desenhar quadros que nunca são exibidos
Eficiência de Recursos
requestAnimationFrame
pausa automaticamente quando a aba do navegador torna-se inativa, economizando ciclos de CPU e vida útil da bateria. setTimeout
continua executando em abas em segundo plano, embora os navegadores possam limitá-lo a uma vez por segundo após cerca de um minuto.
// Loop de animação eficiente em bateria
function gameLoop(timestamp) {
updatePhysics(timestamp);
renderGraphics();
requestAnimationFrame(gameLoop);
}
// Abordagem ineficiente com setTimeout
function inefficientLoop() {
updatePhysics();
renderGraphics();
setTimeout(inefficientLoop, 16); // Tentando 60fps
}
Discover how at OpenReplay.com.
Casos de Uso Práticos
Quando Usar requestAnimationFrame
Use requestAnimationFrame
para qualquer atualização visual:
- Animações de propriedades CSS
- Operações de desenho em Canvas
- Renderização WebGL
- Atualizações de posição DOM
- Indicadores de progresso durante animações
// Implementação de scroll suave
function smoothScrollTo(targetY, duration) {
const startY = window.scrollY;
const distance = targetY - startY;
const startTime = performance.now();
function scroll(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
// Função de easing para movimento mais suave
const easeInOutQuad = progress * (2 - progress);
window.scrollTo(0, startY + distance * easeInOutQuad);
if (progress < 1) {
requestAnimationFrame(scroll);
}
}
requestAnimationFrame(scroll);
}
Quando Usar setTimeout
Escolha setTimeout
para tarefas não-visuais:
- Chamadas de API com atraso
- Debouncing de entrada do usuário
- Operações de polling
- Tarefas agendadas em segundo plano
- Atrasos únicos
// Busca com debounce
let searchTimeout;
function handleSearchInput(query) {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(() => {
performSearch(query);
}, 300);
}
// Lógica de retry com backoff exponencial
function fetchWithRetry(url, attempts = 3, delay = 1000) {
return fetch(url).catch(error => {
if (attempts > 1) {
return new Promise(resolve => {
setTimeout(() => {
resolve(fetchWithRetry(url, attempts - 1, delay * 2));
}, delay);
});
}
throw error;
});
}
Armadilhas Comuns e Soluções
Suposições sobre Taxa de Quadros
Nunca assuma uma taxa de quadros fixa com requestAnimationFrame
. Diferentes telas atualizam em taxas diferentes (60Hz, 120Hz, 144Hz). Sempre use o parâmetro timestamp para animações baseadas em tempo:
let lastTime = 0;
function animate(currentTime) {
const deltaTime = currentTime - lastTime;
lastTime = currentTime;
const element = document.getElementById('moving-element');
const currentLeft = parseFloat(element.style.left) || 0;
// Move 100 pixels por segundo independente da taxa de quadros
const pixelsPerMs = 100 / 1000;
element.style.left = `${currentLeft + pixelsPerMs * deltaTime}px`;
requestAnimationFrame(animate);
}
Vazamentos de Memória
Sempre armazene e limpe IDs de quadros de animação quando componentes são desmontados:
let animationId;
function startAnimation() {
function animate() {
// Lógica de animação aqui
animationId = requestAnimationFrame(animate);
}
animationId = requestAnimationFrame(animate);
}
function stopAnimation() {
if (animationId) {
cancelAnimationFrame(animationId);
animationId = null;
}
}
// Limpa ao descarregar a página
window.addEventListener('beforeunload', stopAnimation);
Guia Rápido de Decisão
Caso de Uso | Melhor Escolha | Motivo |
---|---|---|
Animações suaves | requestAnimationFrame | Sincroniza com atualização da tela |
Renderização Canvas/WebGL | requestAnimationFrame | Previne tearing e jank |
Polling de API | setTimeout | Não vinculado a atualizações visuais |
Debouncing de entrada do usuário | setTimeout | Precisa de controle preciso de atraso |
Barras de progresso durante animação | requestAnimationFrame | Requisito de feedback visual |
Processamento de dados em segundo plano | setTimeout | Continua quando aba inativa |
Loops de jogos | requestAnimationFrame | Desempenho ótimo e vida útil da bateria |
Conclusão
A escolha entre requestAnimationFrame
e setTimeout
não é sobre qual é “melhor”—é sobre usar a ferramenta certa para o trabalho. Para atualizações visuais e animações, requestAnimationFrame
fornece desempenho superior através da sincronização com o navegador. Para necessidades gerais de temporização e tarefas em segundo plano, setTimeout
oferece a flexibilidade e previsibilidade que você precisa. Entender essas diferenças garante que suas aplicações JavaScript entreguem experiências de usuário suaves enquanto gerenciam eficientemente os recursos do sistema.
Perguntas Frequentes
Embora você possa usar setTimeout com um atraso de 16.67ms para aproximar 60fps, ele não sincronizará com o ciclo real de atualização do navegador. Isso leva a quadros perdidos, jank e ciclos de CPU desperdiçados. requestAnimationFrame adapta-se automaticamente à taxa de atualização da tela.
Sim, requestAnimationFrame adapta-se à taxa de atualização do monitor. Em uma tela de 120Hz, ele dispara aproximadamente 120 vezes por segundo. Sempre use o parâmetro timestamp para calcular delta time para velocidade de animação consistente em diferentes telas.
Se seu callback exceder o orçamento do quadro, o navegador pulará quadros para manter a responsividade. Isso causa gagueira visível. Considere otimizar seu código, usar web workers para cálculos pesados, ou reduzir a complexidade da animação.
Gain control over your UX
See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.