Concurrencia y subprocesamiento múltiple en TypeScript: nivel avanzado

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Concurrencia y subprocesamiento múltiple en TypeScript: nivel avanzado



En el desarrollo web moderno y las aplicaciones de servidor en TypeScript, la capacidad de trabajar eficientemente con concurrencia y subprocesamiento múltiple se vuelve críticamente importante. Aunque JavaScript es un lenguaje de un solo hilo, el entorno de ejecución (navegador o Node.js) proporciona herramientas potentes para la ejecución paralela de tareas. En este artículo, exploraremos técnicas avanzadas para trabajar con concurrencia en TypeScript, incluyendo Worker Threads, SharedArrayBuffer, Atomics y patrones de sincronización.



1. Fundamentos básicos: Event Loop y async/await



Antes de sumergirnos en el subprocesamiento múltiple, es importante entender cómo funciona el Event Loop en TypeScript. La concurrencia en un entorno de un solo hilo se logra mediante E/S no bloqueante y operaciones asíncronas.



Consideremos un ejemplo usando Promise.allSettled() para la ejecución concurrente de múltiples tareas asíncronas con manejo de errores:



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 en Node.js: verdadero subprocesamiento múltiple



Para tareas intensivas en CPU (por ejemplo, procesamiento de imágenes, criptografía, cálculos complejos) se necesitan hilos reales. En Node.js, se utilizan Worker Threads para esto.



Creemos un Worker tipado con 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);}


Ahora creemos un administrador para gestionar múltiples 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

Blogs

Book Recommendations