11 de Janeiro de 2025
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
Founder & Engineer
4 min Intermediario Sistemas
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_threadsou 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:
- O problema é espera por I/O ou CPU ocupada?
- A thread principal está livre para continuar atendendo?
- 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_threadssem 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_threadsajudam 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
- Node não executa seu JavaScript em várias threads por padrão, mas isso não significa que o runtime inteiro faz uma coisa por vez.
- Node lida muito bem com concorrência de I/O; o problema aparece quando você prende a thread principal com CPU síncrona.
- Esperar rede, disco e banco é diferente de calcular hash pesado ou processar imagem na thread principal.
- Worker threads entram quando o gargalo é CPU, não como resposta automática para qualquer lentidão.
Checklist de pratica
Use isto ao responder
- Consigo explicar a diferença entre concorrência de I/O e paralelismo de CPU no Node?
- Sei dizer por que uma rota com CPU pesada piora todas as outras requisições?
- Consigo explicar quando `worker_threads` ajudam e quando são exagero?
- Sei responder em entrevista sem repetir só a frase 'Node é single-threaded'?
Você concluiu este artigo
Próximo passo
Como o event loop funciona Próximo passo →Compartilhar esta página
Copie o link manualmente no campo abaixo.