Event Loop do Node.js Explicado para Iniciantes

Backend2026-09-14TryQuickToolBox

Você provavelmente já ouviu que o Node.js é single-threaded e usa um event loop para lidar com milhares de conexões simultâneas. Mas o que isso realmente significa? Se você é novo no Node.js, o event loop pode parecer uma caixa preta misteriosa. Este artigo o explica em linguagem simples, para que você possa escrever código Node.js melhor e mais rápido.

Por que o Event Loop Existe

Servidores web tradicionais (como Apache com PHP) criam uma nova thread ou processo para cada requisição. Isso funciona, mas não escala bem quando você tem muitas conexões simultâneas—cada thread consome memória e CPU. O Node.js adota uma abordagem diferente: ele executa seu JavaScript em uma única thread e usa um event loop para lidar com operações de I/O de forma assíncrona. Isso significa que seu código não espera uma consulta ao banco de dados ou leitura de arquivo terminar; ele continua executando outras tarefas e volta quando o resultado estiver pronto.

Os Componentes Principais

Antes de mergulhar no event loop, vamos esclarecer os principais elementos:

Como o Event Loop Funciona: Um Exemplo Simples

Considere este código:

console.log('Start');

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

console.log('End');

Qual é a saída? Se você adivinhou Start, End, Timeout, você está certo. Mesmo que o timeout seja de 0 milissegundos, o callback não é executado imediatamente. Veja o que acontece:

  1. console.log('Start') é empilhado na call stack, executado e desempilhado.
  2. setTimeout é chamado. O Node.js registra o timer e define para expirar após 0ms. O callback é armazenado e a função retorna.
  3. console.log('End') é empilhado, executado e desempilhado.
  4. Agora a call stack está vazia. O event loop verifica a fase de timers e vê que o timer expirou. Ele move o callback para a call stack, que registra Timeout.

Isso demonstra a natureza não bloqueante: o timer não bloqueia o restante do código.

As Fases do Event Loop

O event loop processa callbacks em várias fases, cada uma com um propósito específico. Entender essas fases ajuda você a prever a ordem de execução.

Fase Descrição
Timers Executa callbacks agendados por setTimeout() e setInterval().
Pending Callbacks Executa callbacks de I/O adiados para a próxima iteração do loop.
Idle, Prepare Usado internamente pelo Node.js.
Poll Recupera novos eventos de I/O; executa callbacks relacionados a I/O (quase todos, exceto timers, setImmediate e callbacks de close).
Check Executa callbacks agendados por setImmediate().
Close Callbacks Executa callbacks de fechamento, por exemplo, socket.on('close', ...).

Entre cada fase, o Node.js verifica microtasks: callbacks de process.nextTick() e Promises. Estas têm prioridade mais alta e são executadas imediatamente após a operação atual terminar, antes de passar para a próxima fase.

Microtasks: nextTick e Promises

Microtasks não fazem parte das fases do event loop; elas são processadas após cada fase e após cada callback. Isso faz com que sejam executadas antes de timers e callbacks de I/O. Por exemplo:

setTimeout(() => console.log('Timeout'), 0);
Promise.resolve().then(() => console.log('Promise'));
process.nextTick(() => console.log('nextTick'));
console.log('Sync');

Saída: Sync, nextTick, Promise, Timeout. O código síncrono executa primeiro, depois as microtasks (nextTick antes de Promises), depois o timer.

Aviso: Chamadas recursivas de process.nextTick() podem esgotar o event loop, impedindo que o I/O aconteça. Use setImmediate() para operações recursivas.

setImmediate vs setTimeout

setImmediate() é projetado para executar um callback imediatamente após a fase de poll atual ser concluída. Em contraste, setTimeout() com 0ms aguarda a próxima fase de timers. A ordem entre eles pode variar dependendo do contexto, mas dentro de um callback de I/O, setImmediate() sempre executa primeiro.

Por que Isso Importa para o Seu Código

Entender o event loop ajuda você a evitar armadilhas comuns:

Exemplo Prático: Leitura de Arquivo Não Bloqueante

Veja como o event loop permite I/O não bloqueante:

const fs = require('fs');

console.log('Before read');

fs.readFile('large-file.txt', 'utf8', (err, data) => {
  if (err) throw err;
  console.log('File read complete');
});

console.log('After read');

Saída: Before read, After read, depois File read complete. A leitura do arquivo acontece em segundo plano, e o callback é enfileirado quando termina. Enquanto isso, outro código pode ser executado.

FAQ

O Node.js é realmente single-threaded?

O Node.js executa seu JavaScript em uma única thread, mas usa múltiplas threads no pool de threads do libuv para I/O de arquivos, DNS e outras operações. Então não é totalmente single-threaded nos bastidores.

Qual é a diferença entre a call stack e o event loop?

A call stack executa código síncrono. O event loop gerencia callbacks assíncronos, movendo-os das filas para a call stack quando ela está vazia.

Posso criar múltiplos event loops?

Não, cada processo Node.js tem um event loop. No entanto, você pode usar worker threads para executar threads JavaScript separadas, cada uma com seu próprio event loop.

Se você está trabalhando com logs de servidor para depurar problemas de desempenho, experimente nosso Analisador de Logs Nginx para analisar e visualizar rapidamente os dados de log.