31 de Dezembro de 2024
Como o event loop funciona
Como pilha, microtasks, timers e ordem de execução se encaixam de verdade.
Andrews Ribeiro
Founder & Engineer
4 min Intermediario Frontend
O problema
Muita explicação sobre event loop para no momento errado.
Ela te entrega:
call stackmicrotask queuemacrotask queue
E depois age como se isso, sozinho, resolvesse a sua vida.
Só que a pergunta real costuma ser outra:
- por que esse
console.logsaiu 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:
- Primeiro roda o que já está na pilha atual.
- Depois o ambiente esvazia as microtasks pendentes.
- 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.thencatchfinally- 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:
- Isso roda agora, de forma síncrona?
- Isso entra na fila de microtasks?
- 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
awaitjoga 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.thenroda antes desetTimeout(..., 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
- Event loop não é uma fila misteriosa; é a coordenação entre código síncrono, microtasks e próximas tasks.
- `setTimeout(..., 0)` não significa execução imediata. Significa só que o timer ficou elegível cedo.
- Microtasks como `Promise.then` e continuação de `await` costumam rodar antes da próxima task.
- Entender ordem de execução ajuda mais no debug de travamento e atraso do que decorar nomes bonitos.
Checklist de pratica
Use isto ao responder
- Consigo explicar a ordem entre código síncrono, `Promise.then` e `setTimeout(..., 0)`?
- Sei dizer por que uma cadeia de microtasks também pode atrasar timers e eventos?
- Consigo relacionar event loop com travamento de UI ou atraso em callbacks?
- Sei explicar esse assunto em entrevista sem depender de diagrama decorado?
Você concluiu este artigo
Próximo passo
Memória sem Mistério Próximo passo →Compartilhar esta página
Copie o link manualmente no campo abaixo.