DEV Community

Cover image for Fundamentos del Runtime de Node.js
Pedro Alvarado
Pedro Alvarado

Posted on

Fundamentos del Runtime de Node.js

Tabla de Contenidos

  1. ¿Qué es Node.js?
  2. Arquitectura del Runtime
  3. El Event Loop
  4. Call Stack, Callback Queue y Microtask Queue
  5. Blocking vs Non-blocking
  6. Manejo de Asincronía
  7. libuv y el Thread Pool
  8. Cuestionario de Entrevista

¿Qué es Node.js?

Node.js es un runtime de JavaScript construido sobre el motor V8 de Chrome. Esto significa que permite ejecutar código JavaScript fuera del navegador, específicamente en el servidor.

Comparación con Python y Ruby

// Node.js
console.log("Hello from Node.js");
Enter fullscreen mode Exit fullscreen mode
# Python
print("Hello from Python")
Enter fullscreen mode Exit fullscreen mode
# Ruby
puts "Hello from Ruby"
Enter fullscreen mode Exit fullscreen mode

Diferencia fundamental:

  • Python/Ruby: Lenguajes interpretados con runtimes single-threaded por defecto (aunque Python tiene threading/multiprocessing y Ruby tiene threads)
  • Node.js: Runtime single-threaded con arquitectura asíncrona no bloqueante basada en eventos

Analogía: El Restaurante

Imagina tres restaurantes diferentes:

Restaurante Python/Ruby (Modelo Síncrono):

  • Un mesero atiende una mesa a la vez
  • Toma el pedido, espera en la cocina hasta que esté listo, sirve, cobra
  • Solo entonces atiende la siguiente mesa
  • Para atender más clientes: contratas más meseros (threads/processes)

Restaurante Node.js (Modelo Asíncrono):

  • Un mesero (single thread) atiende muchas mesas
  • Toma pedidos de múltiples mesas
  • Delega a la cocina (operaciones I/O)
  • Mientras la cocina trabaja, sigue tomando más pedidos
  • Cuando un plato está listo (evento), lo entrega
  • Puede atender cientos de mesas sin contratar más meseros

Arquitectura del Runtime

Node.js está compuesto por varias capas:

┌─────────────────────────────────────┐
│    JavaScript (Tu Código)           │
├─────────────────────────────────────┤
│    Node.js APIs (fs, http, etc)     │
├─────────────────────────────────────┤
│    Node.js Bindings (C++)           │
├─────────────────────────────────────┤
│    V8 Engine          │   libuv     │
│  (JavaScript VM)      │  (Async I/O)│
└─────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Componentes Clave

1. V8 Engine:

  • Motor de JavaScript de Google (escrito en C++)
  • Compila JavaScript a código máquina
  • Maneja la memoria (garbage collection)
  • Ejecuta el código JavaScript

2. libuv:

  • Biblioteca escrita en C
  • Proporciona el Event Loop
  • Maneja operaciones asíncronas (I/O)
  • Implementa el Thread Pool
  • Abstrae diferencias entre sistemas operativos

3. Node.js Bindings:

  • Conecta JavaScript con C++
  • Permite que tu código JS use funcionalidades del sistema operativo

4. Node.js APIs:

  • fs (file system)
  • http/https
  • crypto
  • stream
  • etc.

El Event Loop

El Event Loop es el corazón de Node.js. Es el mecanismo que permite que Node.js realice operaciones no bloqueantes a pesar de que JavaScript es single-threaded.

¿Cómo Funciona?

El Event Loop es un ciclo infinito que ejecuta código, recopila y procesa eventos, y ejecuta subtareas en cola.

// Ejemplo conceptual del Event Loop
while (hayTareasPendientes) {
  ejecutarFasesDelEventLoop();

  if (hayEventosPendientes) {
    procesarEventos();
  }
}
Enter fullscreen mode Exit fullscreen mode

Las 6 Fases del Event Loop

   ┌───────────────────────────┐
┌─>│           timers          │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │     pending callbacks     │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │       idle, prepare       │
│  └─────────────┬─────────────┘      ┌───────────────┐
│  ┌─────────────┴─────────────┐      │   incoming:   │
│  │           poll            │<─────┤  connections, │
│  └─────────────┬─────────────┘      │   data, etc.  │
│  ┌─────────────┴─────────────┐      └───────────────┘
│  │           check           │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
└──┤      close callbacks      │
   └───────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

1. Timers (Temporizadores)

Ejecuta callbacks de setTimeout() y setInterval() cuyo tiempo ha expirado.

setTimeout(() => {
  console.log('Timer ejecutado después de 1000ms');
}, 1000);
Enter fullscreen mode Exit fullscreen mode

Importante: El tiempo especificado es el tiempo mínimo de espera, no garantiza ejecución exacta.

2. Pending Callbacks

Ejecuta callbacks de I/O diferidos de la iteración anterior (errores de TCP, etc).

3. Idle, Prepare

Fase interna de Node.js. No escribes código que se ejecute aquí.

4. Poll (Encuesta)

La fase más importante. Aquí Node.js:

  • Recupera nuevos eventos I/O
  • Ejecuta callbacks relacionados con I/O
  • Puede bloquear esperando eventos si no hay nada más que hacer
const fs = require('fs');

// Este callback se ejecuta en la fase Poll
fs.readFile('/archivo.txt', (err, data) => {
  console.log('Archivo leído');
});
Enter fullscreen mode Exit fullscreen mode

5. Check (Verificación)

Ejecuta callbacks de setImmediate().

setImmediate(() => {
  console.log('Ejecutado en fase Check');
});
Enter fullscreen mode Exit fullscreen mode

6. Close Callbacks

Ejecuta callbacks de cierre (ej: socket.on('close', ...)).

const server = require('http').createServer();

server.on('close', () => {
  console.log('Servidor cerrado');
});
Enter fullscreen mode Exit fullscreen mode

Ejemplo Completo del Event Loop

const fs = require('fs');

console.log('1. Inicio del programa');

// Fase: Timers
setTimeout(() => {
  console.log('2. setTimeout 0ms');
}, 0);

// Fase: Check
setImmediate(() => {
  console.log('3. setImmediate');
});

// Fase: Poll
fs.readFile(__filename, () => {
  console.log('4. readFile callback');

  // Dentro de un callback de I/O:
  setTimeout(() => {
    console.log('5. setTimeout dentro de readFile');
  }, 0);

  setImmediate(() => {
    console.log('6. setImmediate dentro de readFile');
  });
});

console.log('7. Fin del programa');

// Salida:
// 1. Inicio del programa
// 7. Fin del programa
// 2. setTimeout 0ms
// 3. setImmediate
// 4. readFile callback
// 6. setImmediate dentro de readFile
// 5. setTimeout dentro de readFile
Enter fullscreen mode Exit fullscreen mode

¿Por qué este orden?

  1. El código síncrono se ejecuta primero (1, 7)
  2. En la siguiente iteración del Event Loop:
    • setTimeout(0) se ejecuta en fase Timers (2)
    • setImmediate se ejecuta en fase Check (3)
  3. readFile completa en fase Poll (4)
  4. Dentro de un callback de I/O, setImmediate tiene prioridad sobre setTimeout (6, 5)

Call Stack, Callback Queue y Microtask Queue

ƒ

Call Stack (Pila de Llamadas)

El Call Stack es donde JavaScript rastrea la ejecución de funciones.

function tercera() {
  console.log('Tercera función');
}

function segunda() {
  tercera();
}

function primera() {
  segunda();
}

primera();

// Call Stack durante la ejecución:
// │ tercera() │  <- Cima (se ejecuta ahora)
// │ segunda() │
// │ primera() │
// │ main()    │  <- Base
// └───────────┘
Enter fullscreen mode Exit fullscreen mode

Características:

  • LIFO (Last In, First Out)
  • Single-threaded: una sola pila
  • Stack overflow cuando hay demasiadas llamadas anidadas
// Ejemplo de Stack Overflow
function recursiva() {
  recursiva(); // Llamada infinita
}

recursiva(); // RangeError: Maximum call stack size exceeded
Enter fullscreen mode Exit fullscreen mode

Callback Queue (Cola de Callbacks)

También llamada Task Queue o Macrotask Queue. Almacena callbacks listos para ejecutarse.

console.log('1');

setTimeout(() => {
  console.log('2');
}, 0);

console.log('3');

// Salida:
// 1
// 3
// 2
Enter fullscreen mode Exit fullscreen mode

¿Por qué "2" se ejecuta al final?

  1. console.log('1') se ejecuta inmediatamente
  2. setTimeout registra el callback en la Cola
  3. console.log('3') se ejecuta inmediatamente
  4. El Call Stack queda vacío
  5. El Event Loop toma el callback de la Cola y lo ejecuta

Microtask Queue (Cola de Microtareas)

Las microtareas tienen mayor prioridad que los callbacks normales.

Microtareas incluyen:

  • Promises (.then(), .catch(), .finally())
  • process.nextTick() (Node.js específico - mayor prioridad aún)
  • queueMicrotask()
  • MutationObserver (en navegadores)
console.log('1. Script inicio');

setTimeout(() => {
  console.log('2. setTimeout (Macrotask)');
}, 0);

Promise.resolve()
  .then(() => {
    console.log('3. Promise 1 (Microtask)');
  })
  .then(() => {
    console.log('4. Promise 2 (Microtask)');
  });

console.log('5. Script fin');

// Salida:
// 1. Script inicio
// 5. Script fin
// 3. Promise 1 (Microtask)
// 4. Promise 2 (Microtask)
// 2. setTimeout (Macrotask)
Enter fullscreen mode Exit fullscreen mode

El Orden Completo de Ejecución

console.log('1. Sincrónico 1');

setTimeout(() => {
  console.log('2. setTimeout');
}, 0);

Promise.resolve().then(() => {
  console.log('3. Promise');
});

process.nextTick(() => {
  console.log('4. nextTick');
});

console.log('5. Sincrónico 2');

// Salida:
// 1. Sincrónico 1
// 5. Sincrónico 2
// 4. nextTick        <- Mayor prioridad
// 3. Promise         <- Microtask
// 2. setTimeout      <- Macrotask
Enter fullscreen mode Exit fullscreen mode

Regla de oro:

  1. Código sincrónico
  2. process.nextTick()
  3. Microtasks (Promises)
  4. Macrotasks (setTimeout, setImmediate, I/O)

Diagrama Completo

┌─────────────────────────────────────────┐
│         JavaScript Call Stack           │
│  (Ejecuta código sincrónico)            │
└─────────────┬───────────────────────────┘
              │
              ↓ (Stack vacío?)
┌─────────────────────────────────────────┐
│     process.nextTick Queue              │
│  (Se ejecuta ANTES de microtasks)       │
└─────────────┬───────────────────────────┘
              │
              ↓ (Vacío?)
┌─────────────────────────────────────────┐
│         Microtask Queue                 │
│  (Promises, queueMicrotask)             │
└─────────────┬───────────────────────────┘
              │
              ↓ (Vacío?)
┌─────────────────────────────────────────┐
│         Macrotask Queue                 │
│  (setTimeout, setImmediate, I/O)        │
└─────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Blocking vs Non-blocking

Operaciones Bloqueantes (Blocking)

Bloquean el Event Loop hasta que completan.

const fs = require('fs');

console.log('Antes de leer');

// BLOQUEANTE - Bloquea el Event Loop
const data = fs.readFileSync('archivo.txt', 'utf8');
console.log(data);

console.log('Después de leer');

// Ejecución:
// 1. "Antes de leer"
// 2. [ESPERA hasta que el archivo se lea completamente]
// 3. Contenido del archivo
// 4. "Después de leer"
Enter fullscreen mode Exit fullscreen mode

Problemas:

  • El servidor no puede atender otras peticiones mientras espera
  • Si 1000 usuarios hacen peticiones, se formaría una cola

Operaciones No Bloqueantes (Non-blocking)

Delegan el trabajo y continúan ejecutando.

const fs = require('fs');

console.log('Antes de leer');

// NO BLOQUEANTE - Continúa inmediatamente
fs.readFile('archivo.txt', 'utf8', (err, data) => {
  if (err) throw err;
  console.log(data);
});

console.log('Después de leer');

// Ejecución:
// 1. "Antes de leer"
// 2. "Después de leer"
// 3. [Cuando el archivo esté listo] Contenido del archivo
Enter fullscreen mode Exit fullscreen mode

Comparación con Python

Python (Blocking por defecto):

import time

print("Inicio")

# Bloquea el thread durante 2 segundos
time.sleep(2)

print("Fin")

# Salida:
# Inicio
# [espera 2 segundos]
# Fin
Enter fullscreen mode Exit fullscreen mode

Python con asyncio (Non-blocking):

import asyncio

async def main():
    print("Inicio")

    # No bloquea, permite que otras tareas se ejecuten
    await asyncio.sleep(2)

    print("Fin")

asyncio.run(main())
Enter fullscreen mode Exit fullscreen mode

Node.js (Non-blocking por defecto):

console.log("Inicio");

// No bloquea
setTimeout(() => {
  console.log("Fin");
}, 2000);

console.log("Continúa");

// Salida:
// Inicio
// Continúa
// [después de 2 segundos]
// Fin
Enter fullscreen mode Exit fullscreen mode

¿Cuándo Usar Cada Uno?

Operaciones Síncronas (Bloqueantes):

  • ✅ Al inicio de la aplicación (cargar configuración)
  • ✅ Scripts simples que no necesitan concurrencia
  • ✅ Cuando el orden es crítico y no hay alternativa
  • ❌ Durante el manejo de peticiones HTTP

Operaciones Asíncronas (No Bloqueantes):

  • ✅ Operaciones I/O (archivos, bases de datos, red)
  • ✅ Servidores web
  • ✅ Cualquier operación que tome tiempo
  • ✅ Cuando necesitas manejar múltiples operaciones concurrentes

Manejo de Asincronía

Node.js ha evolucionado en cómo maneja la asincronía:

1. Callbacks (El Patrón Original)

const fs = require('fs');

// Patrón Error-First Callback
fs.readFile('archivo1.txt', 'utf8', (err, data1) => {
  if (err) {
    console.error('Error leyendo archivo1:', err);
    return;
  }

  console.log('Archivo 1:', data1);

  // Callback anidado (Callback Hell)
  fs.readFile('archivo2.txt', 'utf8', (err, data2) => {
    if (err) {
      console.error('Error leyendo archivo2:', err);
      return;
    }

    console.log('Archivo 2:', data2);

    // Más anidación...
    fs.readFile('archivo3.txt', 'utf8', (err, data3) => {
      if (err) {
        console.error('Error leyendo archivo3:', err);
        return;
      }

      console.log('Archivo 3:', data3);
    });
  });
});
Enter fullscreen mode Exit fullscreen mode

Problemas:

  • Callback Hell (Pirámide de la perdición)
  • Difícil de leer y mantener
  • Manejo de errores repetitivo

2. Promises (ES6)

Las Promises representan un valor que puede estar disponible ahora, en el futuro, o nunca.

const fs = require('fs').promises;

// Encadenamiento de Promises
fs.readFile('archivo1.txt', 'utf8')
  .then(data1 => {
    console.log('Archivo 1:', data1);
    return fs.readFile('archivo2.txt', 'utf8');
  })
  .then(data2 => {
    console.log('Archivo 2:', data2);
    return fs.readFile('archivo3.txt', 'utf8');
  })
  .then(data3 => {
    console.log('Archivo 3:', data3);
  })
  .catch(err => {
    console.error('Error:', err);
  });
Enter fullscreen mode Exit fullscreen mode

Estados de una Promise

// PENDING (Pendiente)
const promise = new Promise((resolve, reject) => {
  // Haciendo algo asíncrono...
});

// FULFILLED (Cumplida)
const fulfilled = Promise.resolve('Éxito');

// REJECTED (Rechazada)
const rejected = Promise.reject(new Error('Falló'));
Enter fullscreen mode Exit fullscreen mode

Creando Promises

function leerArchivoPromise(filename) {
  return new Promise((resolve, reject) => {
    fs.readFile(filename, 'utf8', (err, data) => {
      if (err) {
        reject(err); // Promise rechazada
      } else {
        resolve(data); // Promise cumplida
      }
    });
  });
}

// Uso
leerArchivoPromise('archivo.txt')
  .then(data => console.log(data))
  .catch(err => console.error(err));
Enter fullscreen mode Exit fullscreen mode

Promise.all() - Paralelismo

const fs = require('fs').promises;

// Leer múltiples archivos en paralelo
Promise.all([
  fs.readFile('archivo1.txt', 'utf8'),
  fs.readFile('archivo2.txt', 'utf8'),
  fs.readFile('archivo3.txt', 'utf8')
])
.then(([data1, data2, data3]) => {
  console.log('Todos leídos:', data1, data2, data3);
})
.catch(err => {
  console.error('Al menos uno falló:', err);
});
Enter fullscreen mode Exit fullscreen mode

Otros Métodos Útiles

// Promise.race() - La primera que complete
Promise.race([
  fetch('https://api1.com/data'),
  fetch('https://api2.com/data')
])
.then(response => console.log('La más rápida respondió'));

// Promise.allSettled() - Espera todas sin importar el resultado
Promise.allSettled([
  Promise.resolve('Éxito 1'),
  Promise.reject('Error 1'),
  Promise.resolve('Éxito 2')
])
.then(results => {
  // results = [
  //   { status: 'fulfilled', value: 'Éxito 1' },
  //   { status: 'rejected', reason: 'Error 1' },
  //   { status: 'fulfilled', value: 'Éxito 2' }
  // ]
});

// Promise.any() - La primera que tenga éxito
Promise.any([
  Promise.reject('Error 1'),
  Promise.resolve('Éxito 1'),
  Promise.resolve('Éxito 2')
])
.then(result => console.log(result)); // 'Éxito 1'
Enter fullscreen mode Exit fullscreen mode

3. Async/Await (ES2017)

Azúcar sintáctico sobre Promises que hace el código asíncrono parecer síncrono.

const fs = require('fs').promises;

async function leerArchivos() {
  try {
    const data1 = await fs.readFile('archivo1.txt', 'utf8');
    console.log('Archivo 1:', data1);

    const data2 = await fs.readFile('archivo2.txt', 'utf8');
    console.log('Archivo 2:', data2);

    const data3 = await fs.readFile('archivo3.txt', 'utf8');
    console.log('Archivo 3:', data3);

    return 'Todos leídos exitosamente';
  } catch (err) {
    console.error('Error:', err);
    throw err;
  }
}

leerArchivos()
  .then(resultado => console.log(resultado))
  .catch(err => console.error('Error final:', err));
Enter fullscreen mode Exit fullscreen mode

Reglas de Async/Await

  1. async siempre retorna una Promise
async function ejemplo() {
  return 'Hola'; // Equivale a: return Promise.resolve('Hola')
}

ejemplo().then(console.log); // 'Hola'
Enter fullscreen mode Exit fullscreen mode
  1. await solo funciona dentro de funciones async
// ❌ Error
function normal() {
  await algo(); // SyntaxError
}

// ✅ Correcto
async function asincrona() {
  await algo();
}
Enter fullscreen mode Exit fullscreen mode
  1. await pausa la ejecución de la función (no del programa)
async function ejemplo() {
  console.log('Antes');
  await delay(1000);
  console.log('Después'); // Se ejecuta 1 segundo después
}

console.log('Inicio');
ejemplo();
console.log('Fin');

// Salida:
// Inicio
// Antes
// Fin
// [después de 1 segundo]
// Después
Enter fullscreen mode Exit fullscreen mode

Paralelismo con Async/Await

// ❌ SECUENCIAL (Lento - suma los tiempos)
async function secuencial() {
  const data1 = await fs.readFile('archivo1.txt', 'utf8'); // 100ms
  const data2 = await fs.readFile('archivo2.txt', 'utf8'); // 100ms
  const data3 = await fs.readFile('archivo3.txt', 'utf8'); // 100ms
  // Total: ~300ms
}

// ✅ PARALELO (Rápido - tiempo del más lento)
async function paralelo() {
  const [data1, data2, data3] = await Promise.all([
    fs.readFile('archivo1.txt', 'utf8'), // 100ms
    fs.readFile('archivo2.txt', 'utf8'), // 100ms
    fs.readFile('archivo3.txt', 'utf8')  // 100ms
  ]);
  // Total: ~100ms
}
Enter fullscreen mode Exit fullscreen mode

Top-Level Await (ES2022)

// Archivo: main.mjs (requiere .mjs o "type": "module" en package.json)

// Antes tenías que envolver en una función async
// async function main() {
//   const data = await fetch('...');
// }
// main();

// Ahora puedes usar await directamente
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
Enter fullscreen mode Exit fullscreen mode

Comparación: Callbacks vs Promises vs Async/Await

// 1. CALLBACKS
function obtenerUsuarioCallback(id, callback) {
  obtenerDeDatabaseCallback(id, (err, usuario) => {
    if (err) return callback(err);

    obtenerPostsCallback(usuario.id, (err, posts) => {
      if (err) return callback(err);

      obtenerComentariosCallback(posts[0].id, (err, comentarios) => {
        if (err) return callback(err);
        callback(null, { usuario, posts, comentarios });
      });
    });
  });
}

// 2. PROMISES
function obtenerUsuarioPromise(id) {
  return obtenerDeDatabasePromise(id)
    .then(usuario => {
      return obtenerPostsPromise(usuario.id)
        .then(posts => {
          return obtenerComentariosPromise(posts[0].id)
            .then(comentarios => {
              return { usuario, posts, comentarios };
            });
        });
    });
}

// 3. ASYNC/AWAIT (Más legible)
async function obtenerUsuarioAsync(id) {
  const usuario = await obtenerDeDatabase(id);
  const posts = await obtenerPosts(usuario.id);
  const comentarios = await obtenerComentarios(posts[0].id);
  return { usuario, posts, comentarios };
}
Enter fullscreen mode Exit fullscreen mode

Manejo de Errores Avanzado

async function manejoErrores() {
  try {
    const resultado = await operacionPeligrosa();
    return resultado;
  } catch (error) {
    // Manejo específico por tipo de error
    if (error.code === 'ENOENT') {
      console.error('Archivo no encontrado');
    } else if (error.code === 'EACCES') {
      console.error('Permiso denegado');
    } else {
      console.error('Error desconocido:', error);
    }
    throw error; // Re-lanzar si es necesario
  } finally {
    // Se ejecuta siempre, haya error o no
    console.log('Limpieza');
  }
}
Enter fullscreen mode Exit fullscreen mode

libuv y el Thread Pool

¿Qué es libuv?

libuv es una biblioteca multi-plataforma escrita en C que proporciona:

  • El Event Loop
  • Operaciones asíncronas de I/O
  • Thread Pool para operaciones que no pueden ser asíncronas
  • Timers
  • Señales de proceso
  • Y más...

El Thread Pool

Aunque Node.js es single-threaded para JavaScript, libuv utiliza un thread pool para operaciones que el sistema operativo no puede hacer de forma asíncrona.

Tamaño por defecto: 4 threads

// Ver el tamaño del thread pool
console.log(process.env.UV_THREADPOOL_SIZE); // undefined = 4 por defecto

// Cambiarlo (debe hacerse antes de cualquier operación que lo use)
process.env.UV_THREADPOOL_SIZE = 8;
Enter fullscreen mode Exit fullscreen mode

Operaciones que Usan el Thread Pool

  1. File System (fs) - Todas las operaciones excepto fs.watch()
  2. DNS lookups - dns.lookup()
  3. Crypto - Operaciones criptográficas pesadas
  4. Zlib - Compresión/descompresión
const crypto = require('crypto');
const fs = require('fs');

// Estas operaciones usan el thread pool
crypto.pbkdf2('password', 'salt', 100000, 512, 'sha512', (err, key) => {
  console.log('Crypto completado');
});

fs.readFile('archivo.txt', (err, data) => {
  console.log('Archivo leído');
});
Enter fullscreen mode Exit fullscreen mode

Operaciones Verdaderamente Asíncronas (No usan Thread Pool)

Estas usan las capacidades asíncronas del sistema operativo:

  1. Networking (TCP/UDP)
  2. HTTP requests
  3. Timers (setTimeout, setInterval)
const http = require('http');

// No usa thread pool - usa epoll/kqueue del sistema operativo
http.get('http://example.com', (res) => {
  console.log('Respuesta recibida');
});
Enter fullscreen mode Exit fullscreen mode

Ejemplo: Impacto del Thread Pool

const crypto = require('crypto');

const inicio = Date.now();

// Ejecutar 8 operaciones pesadas
for (let i = 0; i < 8; i++) {
  crypto.pbkdf2('password', 'salt', 100000, 512, 'sha512', () => {
    console.log(`${i + 1}: ${Date.now() - inicio}ms`);
  });
}

// Con UV_THREADPOOL_SIZE=4 (default):
// 1: 523ms
// 2: 524ms
// 3: 525ms
// 4: 526ms
// 5: 1045ms  <- Segunda tanda
// 6: 1046ms
// 7: 1047ms
// 8: 1048ms

// Con UV_THREADPOOL_SIZE=8:
// 1-8: todos ~520ms (todas en paralelo)
Enter fullscreen mode Exit fullscreen mode

Arquitectura Completa

Tu Código JavaScript
        ↓
    Node.js APIs
        ↓
    ┌───────────────────────┐
    │    Event Loop         │
    │    (libuv)            │
    └───────┬───────────────┘
            │
    ┌───────┴───────────────┐
    │                       │
    ↓                       ↓
Operaciones                Thread Pool
Asíncronas Nativas         (libuv)
(epoll/kqueue)             4 threads default
- Network I/O              - File I/O
- Timers
Enter fullscreen mode Exit fullscreen mode

Top comments (0)