Aprenda / JavaScript do zero

IntermediárioAula 13 de 1617 min0% da trilha

Event loop, microtasks e macrotasks

await parece pausar tudo, mas por baixo é a call stack, a fila de callbacks e a fila de microtasks decidindo quem roda primeiro.

Você já sabe usar Promise e async/await. A pergunta que fica em aberto: JavaScript roda numa única thread — então como uma Promise "espera" alguma coisa sem travar o programa inteiro? A resposta é o event loop.

Call stack — onde o código síncrono roda

A call stack é onde funções são executadas, uma de cada vez, cada uma empilhada sobre a anterior até retornar. Enquanto tem algo na call stack, nada mais roda — é por isso que uma função síncrona muito longa trava a página inteira.

Web APIs — quem cuida do que é lento

setTimeout, fetch e eventos de clique não são executados pelo motor de JavaScript diretamente — são entregues ao navegador (ou ao Node), que cuida da espera por fora da call stack. Quando terminam, o resultado não entra direto na call stack: ele vai pra uma fila, esperando sua vez.

Duas filas: macrotasks e microtasks

Existem duas filas de espera, e elas não têm a mesma prioridade:

// vai pra fila de MACROTASKS (callback queue)
setTimeout(() => console.log("do setTimeout"), 0);

// vai pra fila de MICROTASKS
Promise.resolve().then(() => console.log("do then"));

O event loop é o mecanismo que fica checando: "a call stack está vazia?". Quando está, ele primeiro esvazia toda a fila de microtasks — uma por uma, incluindo novas microtasks que apareçam no meio do processo — e só depois pega um único item da fila de macrotasks.

O exemplo que mostra tudo

console.log("1");

setTimeout(() => console.log("2"), 0);

Promise.resolve().then(() => console.log("3"));

console.log("4");

// ordem real de impressão: 1, 4, 3, 2

"1" e "4" rodam na hora, de forma síncrona, porque estão direto no código principal. Quando a call stack fica vazia, o event loop esvazia a fila de microtasks primeiro — "3" — e só então pega o setTimeout da fila de macrotasks — "2". Isso acontece mesmo com 0 milissegundos de atraso no setTimeout: a fila de microtasks sempre tem prioridade.

await funciona sobre essa mesma base: o código depois de um await vira, por baixo, um .then() — ou seja, entra na fila de microtasks, não na de macrotasks.

Exercício

Qual é a ordem de impressão deste código, e por quê?
console.log("A"); setTimeout(() => console.log("B"), 0); Promise.resolve().then(() => console.log("C")); Promise.resolve().then(() => console.log("D")); console.log("E");

Quiz rápido

Por que uma Promise resolvida roda antes de um setTimeout(fn, 0)?
O que é a call stack no contexto do event loop?