Node.js Event Loop Explicado para Principiantes

Backend2026-09-14TryQuickToolBox

Probablemente hayas oído que Node.js es de un solo hilo y utiliza un event loop para manejar miles de conexiones concurrentes. Pero ¿qué significa eso realmente? Si eres nuevo en Node.js, el event loop puede parecer una caja negra misteriosa. Este artículo lo desglosa en lenguaje sencillo para que puedas escribir código Node.js mejor y más rápido.

Por qué existe el event loop

Los servidores web tradicionales (como Apache con PHP) crean un nuevo hilo o proceso para cada solicitud. Eso funciona, pero no escala bien cuando tienes muchas conexiones concurrentes: cada hilo consume memoria y CPU. Node.js adopta un enfoque diferente: ejecuta tu JavaScript en un solo hilo y utiliza un event loop para manejar operaciones de I/O de forma asíncrona. Esto significa que tu código no espera a que termine una consulta a la base de datos o la lectura de un archivo; continúa ejecutando otras tareas y vuelve cuando el resultado está listo.

Los componentes principales

Antes de sumergirnos en el event loop, aclaremos los actores clave:

Cómo funciona el event loop: un ejemplo simple

Considera este código:

console.log('Start');

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

console.log('End');

¿Cuál es la salida? Si adivinaste Start, End, Timeout, estás en lo correcto. Aunque el timeout es de 0 milisegundos, el callback no se ejecuta inmediatamente. Esto es lo que sucede:

  1. console.log('Start') se apila en el call stack, se ejecuta y se desapila.
  2. Se llama a setTimeout. Node.js registra el temporizador y lo establece para que expire después de 0 ms. El callback se almacena y la función retorna.
  3. console.log('End') se apila, se ejecuta y se desapila.
  4. Ahora el call stack está vacío. El event loop verifica la fase de temporizadores y ve que el temporizador ha expirado. Mueve el callback al call stack, que imprime Timeout.

Esto demuestra la naturaleza no bloqueante: el temporizador no bloquea el resto del código.

Las fases del event loop

El event loop procesa callbacks en varias fases, cada una con un propósito específico. Comprender estas fases te ayuda a predecir el orden de ejecución.

Fase Descripción
Timers Ejecuta callbacks programados por setTimeout() y setInterval().
Pending Callbacks Ejecuta callbacks de I/O diferidos a la siguiente iteración del loop.
Idle, Prepare Usado internamente por Node.js.
Poll Recupera nuevos eventos de I/O; ejecuta callbacks relacionados con I/O (casi todos excepto timers, setImmediate y close callbacks).
Check Ejecuta callbacks programados por setImmediate().
Close Callbacks Ejecuta callbacks de cierre, por ejemplo, socket.on('close', ...).

Entre cada fase, Node.js verifica microtasks: callbacks de process.nextTick() y Promesas. Estos tienen mayor prioridad y se ejecutan inmediatamente después de que la operación actual finaliza, antes de pasar a la siguiente fase.

Microtasks: nextTick y Promesas

Las microtasks no forman parte de las fases del event loop; se procesan después de cada fase y después de cada callback. Esto hace que se ejecuten antes que los timers y los callbacks de I/O. Por ejemplo:

setTimeout(() => console.log('Timeout'), 0);
Promise.resolve().then(() => console.log('Promise'));
process.nextTick(() => console.log('nextTick'));
console.log('Sync');

Salida: Sync, nextTick, Promise, Timeout. El código síncrono se ejecuta primero, luego las microtasks (nextTick antes que Promesas), luego el temporizador.

Advertencia: Las llamadas recursivas a process.nextTick() pueden dejar sin recursos al event loop, impidiendo que ocurra I/O. Usa setImmediate() para operaciones recursivas.

setImmediate vs setTimeout

setImmediate() está diseñado para ejecutar un callback inmediatamente después de que se completa la fase de poll actual. En contraste, setTimeout() con 0 ms espera a la siguiente fase de temporizadores. El orden entre ellos puede variar según el contexto, pero dentro de un callback de I/O, setImmediate() siempre se ejecuta primero.

Por qué esto importa para tu código

Comprender el event loop te ayuda a evitar errores comunes:

Ejemplo práctico: lectura de archivos no bloqueante

Así es como el event loop permite I/O no bloqueante:

const fs = require('fs');

console.log('Before read');

fs.readFile('large-file.txt', 'utf8', (err, data) => {
  if (err) throw err;
  console.log('File read complete');
});

console.log('After read');

Salida: Before read, After read, luego File read complete. La lectura del archivo ocurre en segundo plano y el callback se encola cuando termina. Mientras tanto, otro código puede ejecutarse.

Preguntas frecuentes

¿Node.js es realmente de un solo hilo?

Node.js ejecuta tu JavaScript en un solo hilo, pero utiliza múltiples hilos en el grupo de hilos de libuv para I/O de archivos, DNS y otras operaciones. Así que no es completamente de un solo hilo bajo el capó.

¿Cuál es la diferencia entre el call stack y el event loop?

El call stack ejecuta código síncrono. El event loop gestiona callbacks asíncronos, moviéndolos de las colas al call stack cuando está vacío.

¿Puedo crear múltiples event loops?

No, cada proceso de Node.js tiene un event loop. Sin embargo, puedes usar worker threads para ejecutar hilos de JavaScript separados, cada uno con su propio event loop.

Si estás trabajando con registros de servidor para depurar problemas de rendimiento, prueba nuestro Analizador de Registros de Nginx para analizar y visualizar rápidamente los datos de los registros.