Introducción: Mitos y realidad del multihilo en JavaScript
Tradicionalmente, JavaScript se asocia con un modelo de ejecución de un solo hilo. Muchos desarrolladores, especialmente aquellos que vienen del mundo de C++ o Java, consideran esto una limitación. Sin embargo, el ecosistema moderno de Node.js ofrece herramientas potentes para trabajar con concurrencia y, lo que es más importante, con verdadero multihilo. En este artículo, analizaremos cómo funciona el Event Loop, cuándo usar Worker Threads y cómo construir correctamente aplicaciones de alta carga.
A diferencia del multihilo clásico, donde cada hilo tiene su propia pila y puede ejecutarse en paralelo en diferentes núcleos de la CPU, JavaScript en el navegador y Node.js utiliza un modelo de concurrencia basada en eventos. Sin embargo, con la aparición del módulo worker_threads en Node.js, hemos obtenido la posibilidad de un verdadero paralelismo.
Parte principal
1. Event Loop: El corazón de la concurrencia en Node.js
El Event Loop es un mecanismo que permite a Node.js ejecutar operaciones de entrada/salida (I/O) no bloqueantes, a pesar de que JavaScript es de un solo hilo. Revisa constantemente la cola de tareas y las ejecuta a medida que llegan.
Fases principales del Event Loop:
- Timers — ejecución de callbacks programados mediante
setTimeout()ysetInterval(). - I/O callbacks — procesamiento de operaciones de entrada/salida completadas.
- Idle, prepare — fases internas de Node.js.
- Poll — obtención de nuevos eventos I/O y ejecución de sus callbacks.
- Check — ejecución de callbacks de
setImmediate(). - Close callbacks — procesamiento de eventos de cierre (por ejemplo,
socket.on('close', ...)).
Ejemplo de funcionamiento del Event Loop:
const fs = require('fs');
console.log('Inicio del programa');
setTimeout(() => { console.log('El temporizador se activó');}, 0);
setImmediate(() => { console.log('setImmediate ejecutado');});
fs.readFile(__filename, () => { console.log('Archivo leído');});
console.log('Fin del programa');
// Salida:// Inicio del programa// Fin del programa// setImmediate ejecutado// El temporizador se activó// Archivo leídoEs importante entender: el Event Loop no hace que el código sea multihilo, solo distribuye eficientemente el tiempo entre las tareas. Si una tarea ocupa la CPU durante demasiado tiempo (por ejemplo, un bucle complejo), todo el Event Loop se bloquea.
2. El problema del bloqueo y la solución: Worker Threads
Para tareas intensivas en CPU (cifrado, procesamiento de imágenes, análisis de grandes datos), el Event Loop no es adecuado: bloquearía todo el servidor. Aquí es donde los Worker Threads vienen al rescate.
Worker Threads es un módulo de Node.js que permite crear hilos reales del sistema operativo. Cada worker tiene su propia instancia de V8 y su propio Event Loop, lo que permite ejecutar código en paralelo.
Ejemplo 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 se detuvo con el 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');
// Tarea intensiva en CPUfunction fibonacci(n) { if (n < 2) return n; return fibonacci(n - 1) + fibonacci(n -