Concorrência e Multithreading em JavaScript: do A ao Z
JavaScript historicamente é uma linguagem single-thread, operando dentro do paradigma de programação orientada a eventos. No entanto, o JavaScript moderno (especialmente no ambiente Node.js) oferece ferramentas poderosas para trabalhar com concorrência e multithreading. Neste artigo, vamos explorar como funcionam Event Loop, Promises, async/await, Worker Threads e clusterização, e como usar esses mecanismos para melhorar o desempenho das suas aplicações.
1. Event Loop: a base da concorrência em JavaScript
O Event Loop é o coração do modelo assíncrono do JavaScript. Ele permite processar múltiplas operações sem bloquear o fluxo principal de execução. No Node.js, o Event Loop gerencia filas de tarefas (macrotasks e microtasks), garantindo entrada/saída não bloqueante.
Exemplo de funcionamento do Event Loop:
console.log('Início');
setTimeout(() => { console.log('Timer 1');}, 0);
process.nextTick(() => { console.log('nextTick');});
Promise.resolve().then(() => { console.log('Promise');});
console.log('Fim');
// Saída:// Início// Fim// nextTick// Promise// Timer 1É importante entender a ordem de execução: código síncrono → microtasks (process.nextTick, Promise.then) → macrotasks (setTimeout, setInterval).
2. Promises e async/await: gerenciando a assincronicidade
Promises são objetos que representam o resultado de uma operação assíncrona. Elas permitem evitar o "callback hell" e construir cadeias de chamadas. async/await é um açúcar sintático sobre Promises, tornando o código assíncrono semelhante ao síncrono.
Exemplo com async/await:
const fetchData = async (url) => { const response = await fetch(url); if (!response.ok) { throw new Error(`Erro HTTP: ${response.status}`); } return await response.json();};
const main = async () => { try { const data = await fetchData('https://api.example.com/data'); console.log('Dados recebidos:', data); } catch (error) { console.error('Erro:', error.message); }};
main();Essa abordagem permite executar múltiplas operações assíncronas em paralelo usando Promise.all():
const [user, posts] = await Promise.all([ fetchUser(userId), fetchPosts(userId)]);console.log(user, posts);3. Worker Threads: verdadeiro multithreading no Node.js
A partir do Node.js 10.5.0, o módulo worker_threads oferece a capacidade de criar threads reais para executar tarefas intensivas em CPU. Cada Worker opera em uma thread separada com sua própria instância V8.
Exemplo de criação de Worker:
// main.jsconst { Worker } = require('worker_threads');
const 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}`)); } }); });};
const main = async () => { const result = await runWorker({ iterations: 1000000 }); console.log('Resultado:', result);};
main();// worker.jsconst { parentPort, workerData } = require('worker_threads');
const heavyComputation = (iterations) => { let result = 0; for (let i = 0; i < iterations; i++) { result += Math.sqrt(i); } return result;};
const result = heavyComputation(workerData.iterations);parentPort.postMessage(result);Worker Threads são ideais para tarefas que bloqueiam o Event Loop: processamento de imagens, cálculos matemáticos, análise de arquivos grandes.