DevPath · Aprenda a programar ESPTEN

Assincronia: promessas e async/await

Microtarefas vs macrotarefas

Duas filas, não uma

Antes vimos que setTimeout(fn, 0) é adiado até que o código atual termine. Mas há um detalhe mais fino: o event loop não tem uma fila, mas (pelo menos) duas, e elas não têm a mesma prioridade.

A regra de ouro

Toda vez que a thread principal termina uma tarefa, ela esvazia por completo a fila de microtarefas antes de pegar a próxima macrotarefa. Ou seja: as microtarefas são a "fila expressa" que passa antes; as macrotarefas esperam na fila de trás.

Pense nisso como um caixa de supermercado: a macrotarefa é o próximo cliente com o carrinho cheio, mas antes de cobrar dele, o caixa atende todos os que só vêm "perguntar algo rápido" (as microtarefas). Mesmo que o cliente do carrinho tenha chegado "primeiro" (delay 0), as perguntas rápidas são resolvidas antes.

console.log("1: síncrono");
setTimeout(() => console.log("2: macrotarefa (setTimeout 0)"), 0);
Promise.resolve().then(() => console.log("3: microtarefa (.then)"));
console.log("4: síncrono");
// Ordem de saída: 1, 4, 3, 2

Primeiro roda todo o código síncrono (1, 4). Depois, antes de tocar na macrotarefa do setTimeout, a fila de microtarefas é esvaziada (3). Só então chega a vez da macrotarefa (2).

await também é uma microtarefa

O código que vem depois de um await é retomado como microtarefa. Por isso, na prática, as promessas sempre "vencem" um setTimeout(0) agendado ao mesmo tempo.

Cuidado: se você encadear muitíssimas microtarefas que geram mais microtarefas, pode deixar sem vez as macrotarefas (incluindo a renderização da página). As microtarefas são rápidas, mas não são de graça.

Exemplos

A ordem clássica da entrevista

console.log("A");
setTimeout(() => console.log("B (setTimeout)"), 0);
Promise.resolve().then(() => console.log("C (then)"));
console.log("D");
// Saída: A, D, C, B

Microtarefas encadeadas vêm antes da macrotarefa

setTimeout(() => console.log("macro"), 0);
Promise.resolve()
  .then(() => console.log("micro 1"))
  .then(() => console.log("micro 2"));
console.log("sincrono");
// Saída: sincrono, micro 1, micro 2, macro
Coloque isto em prática

O DevPath é um curso prático: aqui você lê a teoria; no app você a coloca em prática com exercícios que rodam de verdade, offline.

Comece grátis no app →
← async / await e Promise.allCombinadores de promessas →