Introdução: Mitos e Realidade da Multithreading em JavaScript
O JavaScript é tradicionalmente associado a um modelo de execução single-thread. Muitos desenvolvedores, especialmente aqueles vindos do mundo C++ ou Java, consideram isso uma limitação. No entanto, o ecossistema moderno do Node.js oferece ferramentas poderosas para trabalhar com concorrência e, mais importante, com verdadeira multithreading. Neste artigo, vamos analisar como funciona o Event Loop, quando usar Worker Threads e como construir aplicações de alta carga corretamente.
Ao contrário da multithreading clássica, onde cada thread tem sua própria pilha e pode ser executada em paralelo em diferentes núcleos da CPU, o JavaScript no navegador e no Node.js usa o modelo de concorrência baseada em eventos. No entanto, com o surgimento do módulo worker_threads no Node.js, obtivemos a capacidade de paralelismo real.
Parte Principal
1. Event Loop: O Coração da Concorrência no Node.js
O Event Loop é um mecanismo que permite ao Node.js executar operações de entrada e saída (I/O) não bloqueantes, apesar da single-thread do JavaScript. Ele verifica constantemente a fila de tarefas e as executa à medida que chegam.
As principais fases do Event Loop:
- Timers — execução de callbacks agendados via
setTimeout()esetInterval(). - I/O callbacks — tratamento de operações de I/O concluídas.
- Idle, prepare — fases internas do Node.js.
- Poll — obtenção de novos eventos de I/O e execução de seus callbacks.
- Check — execução de callbacks
setImmediate(). - Close callbacks — tratamento de eventos de fechamento (por exemplo,
socket.on('close', ...)).
Exemplo de funcionamento do Event Loop:
const fs = require('fs');
console.log('Início do programa');
setTimeout(() => { console.log('Timer disparou');}, 0);
setImmediate(() => { console.log('setImmediate executado');});
fs.readFile(__filename, () => { console.log('Arquivo lido');});
console.log('Fim do programa');
// Saída:// Início do programa// Fim do programa// setImmediate executado// Timer disparou// Arquivo lidoÉ importante entender: o Event Loop não torna o código multi-thread, ele apenas distribui eficientemente o tempo entre as tarefas. Se uma tarefa ocupar a CPU por muito tempo (por exemplo, um loop complexo), todo o Event Loop é bloqueado.
2. O Problema do Bloqueio e a Solução: Worker Threads
Para tarefas intensivas em CPU (criptografia, processamento de imagens, parsing de grandes dados), o Event Loop não é adequado — ele bloquearia todo o servidor. É aqui que entram as Worker Threads.
Worker Threads é um módulo do Node.js que permite criar threads reais do sistema operacional. Cada worker tem sua própria instância do V8 e Event Loop, permitindo a execução paralela do código.
Exemplo básico de uso de Worker Threads:
// main.jsconst { Worker } = require('worker_threads');
function runWorker(workerData) { return new Promise((resolve, reject) => { const worker = new Worker('./worker.js', { workerData }); worker.on('message', resolve); worker.on('error', reject); worker.on('exit', (code) => { if (code !== 0) { reject(new Error(`Worker parou com código ${code}`)); } }); });}
async function main() { console.log('Iniciando worker...'); const result = await runWorker(42); console.log('Resultado:', result);}
main().catch(console.error);
// worker.jsconst { parentPort, workerData } = require('worker_threads');
// Tarefa intensiva em CPUfunction fibonacci(n) { if (n < 2) return n; return fibonacci(n - 1) + fibonacci(n -