Pular para o conteudo principal

Node não é Single-Threaded do Jeito que Parece

Como separar thread principal, event loop, I/O, libuv e worker threads sem repetir uma frase errada.

Andrews Ribeiro

Andrews Ribeiro

Founder & Engineer

O problema

Muita gente repete que “Node é single-threaded” como se isso explicasse a arquitetura inteira.

Essa frase ajuda no começo, mas estraga o raciocínio quando passa a significar que o runtime inteiro só consegue fazer uma coisa por vez.

Não é assim que o Node funciona de verdade.

Modelo mental

O jeito mais útil de pensar em Node é separar as camadas:

  • o seu JavaScript roda em uma thread principal por padrão
  • o event loop coordena o que entra e sai dessa thread
  • o runtime e o sistema operacional lidam com muita coisa assíncrona sem travar a thread esperando
  • quando você precisa de paralelismo real de CPU, entra em worker_threads ou em outro serviço

Se quiser resumir em uma frase:

Node não faz “uma coisa por vez”. Ele só não sai executando o seu JavaScript em várias threads por padrão.

Quebrando o problema

O JavaScript da aplicação roda em uma thread principal

Esse pedaço da frase está certo.

O problema é parar aí e esquecer todo o resto.

Esperar I/O não é a mesma coisa que queimar CPU

Quando sua aplicação espera:

  • banco
  • disco
  • rede
  • serviço externo

ela não precisa ficar queimando CPU o tempo inteiro para isso.

É exatamente aí que Node costuma ir bem: coordenando muitas esperas concorrentes.

CPU pesada muda completamente o jogo

Quando você coloca trabalho síncrono pesado na thread principal, o problema muda.

Agora não é mais esperar resposta externa.

Agora é:

  • hash pesado
  • compressão
  • parsing caro
  • imagem
  • loop grande demais

Tudo isso prende a thread principal e atrasa o resto.

worker_threads não são enfeite

Quando o gargalo é CPU e faz sentido continuar no mesmo processo, worker_threads entram como ferramenta real.

Mas elas não existem para deixar Node mais profissional.

Elas existem para tirar trabalho pesado da thread que precisa continuar coordenando a aplicação.

O modelo útil para debug

Quando a aplicação engasga, organize assim:

  1. O problema é espera por I/O ou CPU ocupada?
  2. A thread principal está livre para continuar atendendo?
  3. Eu preciso quebrar o trabalho, mover para worker ou até mudar de arquitetura?

Quando você pensa assim, a frase “Node é single-threaded” deixa de ser slogan e vira explicação útil.

Exemplo simples

Imagine esta rota:

app.get('/hash', (req, res) => {
  const result = slowHash(req.query.input)
  res.send(result)
})

Enquanto slowHash calcula, ele prende a thread principal.

O problema não é que “Node não faz concorrência”.

O problema é que você colocou trabalho pesado de CPU exatamente no lugar onde o event loop precisava continuar respirando para coordenar outras requisições.

Enquanto isso roda:

  • outras requisições atrasam
  • callbacks atrasam
  • o processo perde capacidade de resposta

É por isso que a discussão certa não é “Node suporta concorrência ou não?”.

A discussão certa é “concorrência de quê?”.

Erros comuns

  • Tratar a thread principal do JavaScript como se ela fosse o runtime inteiro.
  • Confundir concorrência de I/O com paralelismo real de CPU.
  • Assumir que qualquer carga backend combina automaticamente com Node.
  • Citar worker_threads sem saber em que cenário elas ajudam.

Como um senior pensa

Quem tem mais experiência separa coordenação de computação.

O raciocínio costuma ser:

Node é ótimo para coordenar muita espera concorrente. Se o gargalo virou CPU pesada, eu preciso tirar esse custo da thread principal ou até mover esse pedaço para outro lugar.

Esse é o tipo de julgamento que muda arquitetura, capacity planning e troubleshooting.

O que o entrevistador quer ver

Em entrevista backend mais profunda, o avaliador quer clareza de modelo mental.

  • você separar explicitamente a thread JavaScript da infraestrutura por baixo
  • você entender a diferença entre gargalo de I/O e gargalo de CPU
  • você saber quando worker_threads ajudam e quando são desperdício

Uma resposta forte costuma soar assim:

Node não executa meu JavaScript em várias threads por padrão, mas isso não significa que ele só lida com uma operação por vez. Ele lida bem com muita concorrência de I/O.

O erro é usar esse slogan para esconder que CPU pesada na thread principal continua sendo um gargalo real.

Resumo rápido

O que vale manter na cabeça

Checklist de pratica

Use isto ao responder

Você concluiu este artigo

Próximo artigo Memória sem Mistério Artigo anterior Como o event loop funciona

Continue explorando

Artigos relacionados