Back

requestAnimationFrame vs setTimeout: Quando Usar Cada Um

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 suaves
  • setTimeout 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, enquanto setTimeout 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
}

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 UsoMelhor EscolhaMotivo
Animações suavesrequestAnimationFrameSincroniza com atualização da tela
Renderização Canvas/WebGLrequestAnimationFramePrevine tearing e jank
Polling de APIsetTimeoutNão vinculado a atualizações visuais
Debouncing de entrada do usuáriosetTimeoutPrecisa de controle preciso de atraso
Barras de progresso durante animaçãorequestAnimationFrameRequisito de feedback visual
Processamento de dados em segundo planosetTimeoutContinua quando aba inativa
Loops de jogosrequestAnimationFrameDesempenho ó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.

OpenReplay