← Node & Express

Fundamentos de Node

¿Qué es Node.js y cómo logra atender muchas peticiones al mismo tiempo con un solo hilo?

Ver respuesta — intenta responderla en voz alta primero

Node.js es un entorno de ejecución (runtime) de JavaScript que corre fuera del navegador, construido sobre el motor V8 de Chrome. Usa un modelo single-thread (un solo hilo) con un event loop, y trabaja de forma orientada a eventos (event-driven) y no bloqueante (non-blocking). Gracias a esto puede manejar miles de conexiones simultáneas sin crear un hilo por cada una.

Antes, JavaScript solo vivía dentro del navegador. Node.js tomó el motor V8 (el que ejecuta JavaScript en Google Chrome) y lo empaquetó para que pudieras correr JavaScript directamente en tu computadora o servidor. Así puedes escribir el backend de una aplicación (APIs, servidores web) usando el mismo lenguaje que en el frontend.

Node se apoya en tres ideas clave:

  • Un solo hilo (single-thread): tu código JavaScript corre en un único hilo principal. No hay 100 hilos atendiendo 100 usuarios; hay uno solo coordinando todo.
  • Event loop: es un ciclo que está siempre atento a "¿hay algo que hacer?". Cuando una tarea termina (por ejemplo, terminó de leerse un archivo), el event loop toma el resultado y ejecuta la función que debía correr después.
  • No bloqueante (non-blocking): cuando Node necesita algo lento (leer un archivo, consultar una base de datos, esperar la red), no se queda esperando de brazos cruzados. Delega esa tarea al sistema (a través de la librería libuv), sigue atendiendo otras peticiones, y cuando la tarea lenta termina, se le avisa con un evento.

¿Por qué un solo hilo puede atender a muchos usuarios? Porque la mayoría del trabajo de un servidor no es cálculo, es esperar: esperar la base de datos, esperar la red, esperar el disco. Node no desperdicia el hilo esperando; lo usa para atender a otros mientras las operaciones lentas ocurren "por fuera". Es como un mesero que toma tu orden, la manda a la cocina, y en vez de quedarse parado frente a la cocina, atiende a otras mesas hasta que tu platillo esté listo.

Sus casos de uso ideales son:

  • APIs REST y servicios web (muchas peticiones cortas).
  • Aplicaciones en tiempo real (chats, notificaciones, juegos) con WebSockets.
  • Microservicios y gateways.
  • Herramientas de línea de comandos.

No es la mejor opción para trabajo intensivo de CPU (procesar video, cálculos matemáticos pesados), porque eso sí bloquea el único hilo.

// Un servidor HTTP mínimo, sin librerías externas.
const http = require("http");

// createServer recibe una función que se ejecuta en CADA petición.
const server = http.createServer((req, res) => {
  res.writeHead(200, { "Content-Type": "text/plain" });
  res.end("Hola desde Node.js\n");
});

// El servidor "escucha" en el puerto 3000.
server.listen(3000, () => {
  console.log("Servidor corriendo en http://localhost:3000");
});

// Node NO termina aquí: el event loop se queda vivo esperando peticiones.
// Un solo hilo atiende a todos los usuarios que lleguen.

Pensar que "single-thread" significa que Node solo puede hacer una cosa a la vez y por eso es lento. En realidad, el hilo único solo ejecuta tu código JavaScript; las operaciones de entrada/salida (I/O) las maneja libuv en segundo plano, usando un pool de hilos del sistema operativo. Por eso Node es rapidísimo con tareas de I/O, aunque tu JavaScript corra en un solo hilo. Lo que sí lo frena es meter cálculos pesados de CPU en ese hilo, porque ahí sí bloqueas todo.

"Node.js es un runtime que permite ejecutar JavaScript fuera del navegador, construido sobre el motor V8 de Chrome. Su modelo es de un solo hilo con un event loop, orientado a eventos y no bloqueante. La clave es que Node no se queda esperando en operaciones lentas como leer disco o consultar una base de datos: delega esas tareas a libuv y sigue atendiendo otras peticiones, y cuando la operación termina ejecuta el callback correspondiente. Por eso puede atender miles de conexiones con un solo hilo, porque la mayor parte del tiempo de un servidor se va en esperar I/O, no en calcular. Lo uso para APIs, microservicios y aplicaciones en tiempo real; lo que evitaría es meterle tareas pesadas de CPU en el hilo principal."

Reto rápido

Si un solo hilo atiende todas las peticiones, ¿qué pasa si dentro de un manejador de petición pongo un while que tarda 10 segundos calculando algo?

Ver respuesta

Bloqueas el event loop por completo. Durante esos 10 segundos, Node no puede atender ninguna otra petición: todos los demás usuarios quedan esperando, porque el único hilo está ocupado con tu while. Este es el peligro del trabajo pesado de CPU en Node. Las soluciones son sacar ese cálculo del hilo principal usando Worker Threads, un proceso hijo (child_process), o una cola de trabajos en un servicio aparte.