Concorrência e Multithreading em TypeScript: Nível Avançado
No desenvolvimento web moderno e em aplicações servidoras em TypeScript, a capacidade de trabalhar eficientemente com concorrência e multithreading torna-se criticamente importante. Embora o JavaScript seja uma linguagem single-threaded, o ambiente de execução (navegador ou Node.js) fornece ferramentas poderosas para execução paralela de tarefas. Neste artigo, exploraremos técnicas avançadas de trabalho com concorrência em TypeScript, incluindo Worker Threads, SharedArrayBuffer, Atomics e padrões de sincronização.
1. Fundamentos: Event Loop e async/await
Antes de mergulhar no multithreading, é importante entender como o Event Loop funciona no TypeScript. A concorrência em um ambiente single-threaded é alcançada através de I/O não bloqueante e operações assíncronas.
Vamos considerar um exemplo usando Promise.allSettled() para execução concorrente de várias tarefas assíncronas com tratamento de erros:
interface TaskResult<T> { status: 'fulfilled' | 'rejected'; value?: T; reason?: Error;}
async function runConcurrentTasks<T>( tasks: Promise<T>[]): Promise<TaskResult<T>[]> { const results = await Promise.allSettled(tasks); return results.map(result => { if (result.status === 'fulfilled') { return { status: 'fulfilled', value: result.value }; } else { return { status: 'rejected', reason: result.reason }; } });}
// Usoconst task1 = fetchData('/api/users');const task2 = fetchData('/api/posts');const task3 = fetchData('/api/comments');
const results = await runConcurrentTasks([task1, task2, task3]);2. Worker Threads no Node.js: Multithreading Real
Para tarefas intensivas em CPU (por exemplo, processamento de imagens, criptografia, cálculos complexos), são necessárias threads reais. No Node.js, isso é feito usando Worker Threads.
Vamos criar um Worker tipado com TypeScript:
// worker.tsimport { parentPort, workerData } from 'worker_threads';
interface WorkerInput { data: number[]; operation: 'sum' | 'multiply';}
interface WorkerOutput { result: number; duration: number;}
if (parentPort) { const input: WorkerInput = workerData; const start = performance.now(); let result: number; if (input.operation === 'sum') { result = input.data.reduce((acc, val) => acc + val, 0); } else { result = input.data.reduce((acc, val) => acc * val, 1); } const output: WorkerOutput = { result, duration: performance.now() - start }; parentPort.postMessage(output);}Agora vamos criar um gerenciador para controlar vários Workers:
// threadPool.tsimport { Worker } from 'worker_threads';import path from 'path';
class ThreadPool<TInput, TOutput> { private workers: Worker[] = []; private taskQueue: Array<{ input: TInput; resolve: (value: TOutput) => void; reject: (reason?: any) => void; }> = [];
constructor( private workerScript: string, private poolSize: number = 4 ) { this.initialize(); }
private initialize(): void { for (let i = 0; i < this.poolSize; i++) { const worker = new Worker(this.workerScript); worker.on('message', (result: TOutput) => { const task = this.taskQueue.shift(); if (task) { task.resolve(result); } worker.postMessage(this.taskQueue[0]?.input); });
worker.on('error', (error) => { const task = this.taskQueue.shift(); if (task) { task.reject(error); } });
this.workers.push(worker); } }
async execute(input: TInput): Promise<TOutput> { return new Promise((resolve, reject) => { this.ta