Entendendo Macrotarefas e Microtarefas no JavaScript

Sabe por que Promise executa antes de setTimeout(0)? Entenda como o event loop do JavaScript organiza macrotarefas, microtarefas e o papel do await e queueMicrotask.

Por que isso importa?

O JavaScript é uma linguagem single-threaded — executa tudo em uma única thread, um passo de cada vez.

O event loop é o sistema responsável por organizar a ordem de execução do seu código. Compreender o que é macrotarefa e microtarefa nesse contexto é fundamental para entender por que:

  • um setTimeout(0) executa depois de uma Promise resolvida — mesmo com delay zero;
  • await não pausa “tudo”, apenas suspende o método atual e agenda sua continuação como microtarefa.

[INSERIR LINK INTERNO PARA o post sobre Antigravity — que aborda como IAs lidam com código assíncrono]


O que é uma Macrotarefa

Uma macrotarefa é um bloco completo de execução do JavaScript.

➡️ É tudo o que o motor JavaScript processa “de uma vez só”, antes de verificar a fila de microtarefas e pegar o próximo item da fila principal.

Exemplos que criam novas macrotarefas:

  • Clique do usuário (click)
  • setTimeout, setInterval
  • Conclusão de uma requisição HTTP

O que é uma Microtarefa

Uma microtarefa é uma operação que o JavaScript executa imediatamente após a macrotarefa atual terminar — antes de iniciar qualquer nova macrotarefa da fila.

São ações leves, normalmente criadas por Promises e mecanismos assíncronos internos.

Exemplos de microtarefas:

  • .then() e .catch() de uma Promise
  • await
  • queueMicrotask()

Entendendo a ordem de execução

🔹 Exemplo 1 – sem await

console.log('A'); // síncrono → parte da macrotarefa atual

setTimeout(() => console.log('B'), 0); // nova macrotarefa

Promise.resolve().then(() => console.log('C')); // microtarefa

console.log('D'); // ainda na mesma macrotarefa

Saída real:

A
D
C
B

Explicando:

  1. A e D → executam imediatamente (síncrono, mesma macrotarefa)
  2. C → é uma microtarefa, roda logo após o código síncrono terminar
  3. B → é nova macrotarefa, roda depois que todas as microtarefas acabam

🔹 Exemplo 2 – com await

async function exemplo() {
  console.log('1'); // síncrono (mesma macrotarefa)

  await Promise.resolve(); // pausa aqui → cria microtarefa

  console.log('2'); // volta aqui numa microtarefa
}

exemplo();

setTimeout(() => console.log('3'), 0); // nova macrotarefa

console.log('4'); // síncrono (mesma macrotarefa)

Saída real:

1
4
2
3

Explicando:

  1. 1 e 4 → são parte da mesma macrotarefa (síncrono)
  2. O await pausa o método e agenda a continuação (2) como microtarefa
  3. 3 → é uma nova macrotarefa (vem do setTimeout)

Além de usar Promise.resolve().then(...), o JavaScript também oferece uma API própria para criar microtarefas:

queueMicrotask(() => {
  console.log('microtask');
});

O que o queueMicrotask faz?

  • Agenda a função na fila de microtarefas
  • Executa logo após o código síncrono da macrotarefa atual
  • Executa antes de qualquer nova macrotarefa (setTimeout, eventos, etc.)

O comportamento é o mesmo de um .then() de Promise.


Então por que usar queueMicrotask?

Porque ele é:

VantagemPor quê
🎯 SemânticoDeixa claro que a intenção é criar uma microtarefa
🧼 Mais diretoNão cria uma Promise desnecessária
⚠️ Mais seguroErros não viram unhandled promise rejection

Exemplo equivalente:

// Microtask usando Promise
Promise.resolve().then(() => console.log('A'));

// Microtask explícita
queueMicrotask(() => console.log('A'));

Ambos rodam no mesmo momento do event loop.


Quando preferir queueMicrotask

Use queueMicrotask quando você quer:

  • Garantir execução logo após o código atual
  • Manter a ordem correta sem atrasos artificiais
  • Evitar o uso de setTimeout(0) apenas para “organizar” a execução

Em código moderno e orientado a performance, queueMicrotask costuma ser a opção mais semântica e previsível.


Resumo rápido

TipoExemploQuando executa
MacrotarefasetTimeout, setInterval, eventos (click, load), fetch concluídoCada rodada do event loop
MicrotarefaPromise.then, await, queueMicrotaskLogo depois do código síncrono da macrotarefa atual

Dica prática pra lembrar

Macrotarefa: “Algo externo que o JavaScript espera (evento, timer, rede)”

Microtarefa: “Coisas internas que precisam terminar antes de seguir (Promises, awaits)“


Conclusão

SituaçãoMelhor opçãoMotivo
Garantir execução logo depois do código atualqueueMicrotask / Promise.resolve().then(...)É microtarefa: roda sem atraso e mantém ordem
Deixar o navegador respirar/pintar antes🕒 setTimeout(..., 0)Cria nova macrotarefa, permite repaint
Evitar travar a UI com loops grandes🧩 setTimeout fatiando tarefasEntrega o controle de volta ao event loop

Se a mesma ordem é alcançada com Promise ou setTimeout(0), prefira Promise/microtarefa — é semanticamente correto, mais previsível e mais performático no event loop do JavaScript.