Pular para o conteudo principal

Como o event loop funciona

Como pilha, microtasks, timers e ordem de execução se encaixam de verdade.

Andrews Ribeiro

Andrews Ribeiro

Founder & Engineer

O problema

Muita explicação sobre event loop para no momento errado.

Ela te entrega:

  • call stack
  • microtask queue
  • macrotask queue

E depois age como se isso, sozinho, resolvesse a sua vida.

Só que a pergunta real costuma ser outra:

  • por que esse console.log saiu antes do outro?
  • por que o setTimeout(..., 0) não foi “na hora”?
  • por que o código parece assíncrono, mas ainda bloqueia tudo?

Se a teoria não te ajuda a prever ordem de execução, ela ainda não virou entendimento.

Modelo mental

O modelo mental mais útil é este:

  1. Primeiro roda o que já está na pilha atual.
  2. Depois o ambiente esvazia as microtasks pendentes.
  3. Só então ele pega a próxima task agendada.

Em português claro:

  • código síncrono roda agora
  • microtask roda logo depois do bloco atual terminar
  • timer, evento e trabalho agendado para depois esperam a próxima volta

Se quiser resumir em uma frase:

O event loop decide a ordem do “agora”, do “daqui a pouco” e do “na próxima volta”.

Quebrando o problema

Primeiro: o que roda agora?

Código síncrono entra aqui.

Enquanto ele não termina:

  • timer não entra
  • evento não entra
  • microtask não interrompe no meio

É por isso que um bloco pesado ainda consegue travar tudo, mesmo em código cheio de async.

Depois: o que vira microtask?

Entram aqui coisas como:

  • Promise.then
  • catch
  • finally
  • continuação depois de await

Essas continuações não entram em paralelo.

Elas só ganham prioridade assim que a pilha atual fica vazia.

Só depois: o que espera a próxima task?

Entram aqui coisas como:

  • setTimeout
  • callbacks de I/O
  • eventos do ambiente

É por isso que setTimeout(..., 0) continua chegando depois de microtasks pendentes.

O 0 não significa “agora”.

Significa só “pode considerar isso cedo”.

O truque para prever ordem de execução

Quando quiser descobrir a ordem, faça só estas três perguntas:

  1. Isso roda agora, de forma síncrona?
  2. Isso entra na fila de microtasks?
  3. Isso agenda trabalho para a próxima task?

Se você consegue classificar o trecho assim, a maior parte do “JavaScript fez magia” desaparece.

Exemplo simples

Olhe este exemplo:

console.log('inicio')

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

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

console.log('fim')

A saída é esta:

inicio
fim
promise
timeout

O motivo é simples:

  • console.log('inicio') roda na hora.
  • setTimeout(..., 0) não roda na hora. Ele só agenda uma próxima task.
  • Promise.resolve().then(...) agenda uma microtask.
  • console.log('fim') ainda faz parte do bloco síncrono atual.
  • Quando a pilha esvazia, o ambiente roda as microtasks pendentes.
  • Só depois ele pega a próxima task agendada.

O 0 no setTimeout não significa “agora”.

Significa só que o timer ficou elegível cedo. Mesmo assim, ele ainda precisa esperar:

  • o código síncrono atual acabar
  • as microtasks pendentes serem processadas

Erros comuns

  • Dizer que promise é “mais rápida” do que timer. O ponto aqui não é velocidade. É prioridade.
  • Achar que setTimeout(..., 0) significa execução imediata.
  • Achar que await joga seu código para outra thread.
  • Esquecer que microtasks demais também atrasam timers, eventos e renderização.

Como um senior pensa

Quem tem mais repertório pensa menos em nome de fila e mais em consequência prática.

O raciocínio costuma ser este:

O que está segurando a thread agora? Código síncrono? Uma cadeia de microtasks? Trabalho pesado que eu deveria quebrar?

Essa pergunta é melhor porque leva para ação real:

  • quebrar trabalho pesado
  • reduzir bloqueio síncrono
  • evitar enfileirar microtask sem necessidade
  • explicar ordem de execução sem chute

O que o entrevistador quer ver

Em entrevista, o ponto não é ver se você sabe repetir um diagrama de blog.

O que normalmente conta mais:

  • você prever a ordem de execução de um exemplo simples sem chutar
  • você explicar por que Promise.then roda antes de setTimeout(..., 0)
  • você conectar o conceito a bug real, como callback atrasando ou UI travando

Uma resposta forte costuma soar assim:

Eu separo o que roda agora, o que vira microtask e o que fica para a próxima task. A partir daí, a ordem deixa de ser chute.

Event loop não é para impressionar com jargão. É para prever comportamento quando o código começa a ficar estranho.

Resumo rápido

O que vale manter na cabeça

Checklist de pratica

Use isto ao responder

Você concluiu este artigo

Próximo artigo Node não é Single-Threaded do Jeito que Parece Artigo anterior Escrever Código que Gente Consegue Entender

Continue explorando

Artigos relacionados