Como Usar Event Loop Avançado: Microtasks, Macrotasks e requestAnimationFrame em Produção Já leu

O que é Event Loop e sua Estrutura Fundamental O Event Loop é o mecanismo central que permite ao JavaScript executar código assíncrono em uma linguagem single-threaded. Compreender sua estrutura é essencial para escrever código performático e previsível. O Event Loop não é parte da especificação do JavaScript em si, mas sim da implementação das engines (como V8 do Chrome), trabalhando em conjunto com as APIs fornecidas pelo ambiente (browser ou Node.js). A execução segue um ciclo: o Event Loop verifica a Call Stack, executa o código síncrono, depois processa as filas de microtasks e macrotasks. Esse processo se repete continuamente. A ordem de execução é: Call Stack → Microtasks → Rendering → Macrotasks → (volta ao início). Compreender essa sequência é crucial para prever quando seu código será executado. Microtasks vs Macrotasks: A Hierarquia de Prioridades O que são Microtasks? Microtasks têm prioridade máxima e são executadas após cada macrotask. Elas incluem: , , , e . A característica

O que é Event Loop e sua Estrutura Fundamental

O Event Loop é o mecanismo central que permite ao JavaScript executar código assíncrono em uma linguagem single-threaded. Compreender sua estrutura é essencial para escrever código performático e previsível. O Event Loop não é parte da especificação do JavaScript em si, mas sim da implementação das engines (como V8 do Chrome), trabalhando em conjunto com as APIs fornecidas pelo ambiente (browser ou Node.js).

A execução segue um ciclo: o Event Loop verifica a Call Stack, executa o código síncrono, depois processa as filas de microtasks e macrotasks. Esse processo se repete continuamente. A ordem de execução é: Call Stack → Microtasks → Rendering → Macrotasks → (volta ao início). Compreender essa sequência é crucial para prever quando seu código será executado.

Microtasks vs Macrotasks: A Hierarquia de Prioridades

O que são Microtasks?

Microtasks têm prioridade máxima e são executadas após cada macrotask. Elas incluem: Promise.then(), Promise.catch(), Promise.finally(), MutationObserver e queueMicrotask(). A característica distintiva é que todas as microtasks na fila são processadas antes de qualquer macrotask.

console.log('1. Sincronizado');

setTimeout(() => {
  console.log('2. Macrotask (setTimeout)');
}, 0);

Promise.resolve()
  .then(() => {
    console.log('3. Microtask (Promise)');
  })
  .then(() => {
    console.log('4. Microtask (Promise 2)');
  });

console.log('5. Sincronizado 2');

// Saída:
// 1. Sincronizado
// 5. Sincronizado 2
// 3. Microtask (Promise)
// 4. Microtask (Promise 2)
// 2. Macrotask (setTimeout)

O que são Macrotasks?

Macrotasks incluem: setTimeout(), setInterval(), setImmediate() (Node.js), eventos DOM (click, load), e requisições HTTP. Apenas uma macrotask é executada por ciclo do Event Loop. Após cada macrotask, todas as microtasks pendentes são processadas antes de qualquer macrotask subsequente.

setTimeout(() => {
  console.log('Macrotask 1');
  Promise.resolve().then(() => console.log('Microtask após Macrotask 1'));
}, 0);

setTimeout(() => {
  console.log('Macrotask 2');
}, 0);

// Saída:
// Macrotask 1
// Microtask após Macrotask 1
// Macrotask 2

Essa hierarquia existe porque Promises são fundamentais para o modelo assíncrono moderno, enquanto macrotasks são operações mais pesadas que exigem controle de quando são executadas.

requestAnimationFrame: O Timing Perfeito para Animações

Onde se Encaixa no Event Loop?

requestAnimationFrame() (rAF) não é exatamente um microtask nem um macrotask. É executado entre as microtasks e a renderização, garantindo que seu código rode sincronizado com a taxa de refresh da tela (60fps ou 120fps). Isso o torna ideal para animações e manipulação do DOM.

console.log('1. Início');

setTimeout(() => {
  console.log('2. setTimeout');
}, 0);

requestAnimationFrame(() => {
  console.log('3. requestAnimationFrame');
});

Promise.resolve().then(() => {
  console.log('4. Promise');
});

console.log('5. Fim síncrono');

// Saída:
// 1. Início
// 5. Fim síncrono
// 4. Promise
// 3. requestAnimationFrame (antes da renderização)
// 2. setTimeout

Exemplo Prático: Animação Eficiente

let position = 0;

function animate() {
  position += 5;
  const element = document.getElementById('box');
  element.style.left = position + 'px';

  if (position < 300) {
    requestAnimationFrame(animate);
  }
}

// Usar rAF é mais eficiente que setInterval
// porque sincroniza com a renderização do navegador
requestAnimationFrame(animate);

// Evite isto:
// setInterval(() => {
//   position += 5;
//   element.style.left = position + 'px';
// }, 16); // Pode desincronizar do refresh

Casos Práticos: Integrando Tudo Junto

Caso 1: Carregamento de Dados com Animação

async function fetchAndAnimate() {
  console.log('1. Iniciando fetch');

  // Macrotask: requisição HTTP
  const response = await fetch('/api/data');

  // Microtask: processamento do Promise
  const data = await response.json();
  console.log('2. Dados recebidos');

  // rAF: renderização suave
  requestAnimationFrame(() => {
    document.getElementById('content').innerHTML = data.message;
    console.log('3. DOM atualizado');
  });
}

// setTimeout garante execução assíncrona
setTimeout(() => {
  fetchAndAnimate();
}, 0);

Caso 2: Batching de Atualizações

const updates = [];

function scheduleUpdate(callback) {
  updates.push(callback);

  // Microtask: agrupa múltiplas atualizações
  Promise.resolve().then(() => {
    console.log(`Processando ${updates.length} atualizações`);
    updates.forEach(cb => cb());
    updates.length = 0;
  });
}

// Chamadas síncronas
scheduleUpdate(() => console.log('Update 1'));
scheduleUpdate(() => console.log('Update 2'));
scheduleUpdate(() => console.log('Update 3'));

// Saída:
// Processando 3 atualizações
// Update 1
// Update 2
// Update 3

Esse padrão é usado internamente por frameworks como React para otimizar renderizações.

Conclusão

Os três conceitos principais que você deve dominar são: (1) Microtasks sempre executam antes de macrotasks, garantindo que Promises sejam processadas com alta prioridade; (2) requestAnimationFrame sincroniza seu código com a renderização do navegador, tornando-o essencial para animações e atualizações visuais eficientes; (3) Compreender essa ordem de execução permite otimizar performance, evitar race conditions e escrever código previsível. Na prática, use Promises para lógica assíncrona crítica, setTimeout para atrasar execução, e rAF para qualquer coisa visual.

Referências


Artigos relacionados