Concorrência e Multithreading em TypeScript: da teoria à prática
Aplicações web modernas exigem alta performance. O TypeScript, executado sobre o Node.js, fornece ferramentas poderosas para processar múltiplas tarefas simultaneamente. Neste artigo, vamos explorar como a concorrência e o multithreading funcionam em TypeScript e aprender a aplicá-los na prática.
Concorrência vs Paralelismo: diferenças-chave
Antes de nos aprofundarmos no código, é importante entender a diferença entre concorrência e paralelismo.
- Concorrência — é a capacidade de um programa processar várias tarefas, alternando entre elas. Em JavaScript/TypeScript, isso é implementado através do Event Loop e operações assíncronas.
- Paralelismo — é a execução simultânea de várias tarefas em diferentes núcleos do processador. No Node.js, isso é alcançado através do Worker Threads.
TypeScript (assim como JavaScript) é single-threaded por natureza, mas graças à assincronicidade e ao Worker Threads, podemos usar ambas as abordagens de forma eficaz.
Assincronicidade: a base da concorrência em TypeScript
A programação assíncrona é o primeiro passo para a concorrência. Vamos analisar os principais mecanismos.
Promises e async/await
O TypeScript moderno usa ativamente async/await para trabalhar com operações assíncronas. Isso permite escrever código que parece síncrono, mas é executado de forma concorrente.
// Exemplo de função assíncronaasync function fetchData(url: string): Promise<any> { const response = await fetch(url); return response.json();}
// Execução concorrente de várias requisiçõesasync function fetchAllData(): Promise<void> { const [users, posts, comments] = await Promise.all([ fetchData('/api/users'), fetchData('/api/posts'), fetchData('/api/comments') ]); console.log('Todos os dados carregados concorrentemente');}Promise.all permite iniciar várias operações assíncronas simultaneamente, o que acelera significativamente a execução em comparação com a execução sequencial.
Tratamento de erros em código assíncrono
É importante tratar corretamente os erros durante a execução concorrente:
async function safeFetchAll(): Promise<void> { try { const results = await Promise.allSettled([ fetchData('/api/users'), fetchData('/api/invalid'), fetchData('/api/posts') ]); results.forEach((result, index) => { if (result.status === 'fulfilled') { console.log(`Requisição ${index} bem-sucedida:`, result.value); } else { console.error(`Requisição ${index} falhou:`, result.reason); } }); } catch (error) { console.error('Erro crítico:', error); }}Worker Threads: verdadeiro multithreading em TypeScript
Para tarefas intensivas de CPU (processamento de imagens, cálculos complexos), apenas a assincronicidade não é suficiente. É aqui que entram os Worker Threads — threads de execução reais.
Criação de um Worker Thread
Vamos considerar um exemplo de cálculo de números de Fibonacci em uma thread separada:
// fibonacci.worker.tsimport { parentPort } from 'worker_threads';
function fibonacci(n: number): number { if (n <= 1) return n; return fibonacci(n - 1) + fibonacci(n - 2);}
parentPort?.on('message', (n: number) => { const result = fibonacci(n); parentPort?.postMessage(result);});// main.tsimport { Worker } from 'worker_threads';import path from 'path';
function runFibonacci(n: number): Promise<number> { return new Promise((resolve, reject) => { const worker = new Worker(path.resolve(__dirname, 'fibonacci.worker.ts')); worker.postMessage(n); worker.on('message', (result: number) => { resolve(result); worker.terminate(); }); worker.on('er
```