</> HTML5Advent
ENFRESDEITPT

// js

¿Qué es el event loop en JavaScript? El asíncrono explicado

JavaScript se ejecuta en un solo hilo y, aun así, gestiona temporizadores, red y clics sin congelarse. El event loop es la clave: la pila de llamadas, las colas de tareas y microtareas, y por qué las promesas se ejecutan antes que setTimeout.

Código JavaScript en color mostrado en la pantalla de un ordenador

JavaScript tiene una peculiaridad famosa: ejecuta tu código en un solo hilo, haciendo una cosa a la vez — y, sin embargo, puede esperar una petición de red, ejecutar un temporizador y responder a clics sin congelarse jamás. Lo que hace esto posible es el event loop. Entenderlo es lo que convierte el JavaScript asíncrono de confuso a evidente.

Un hilo, una pila de llamadas

JavaScript ejecuta el código en una única pila de llamadas (call stack): las funciones se apilan al llamarlas y se desapilan al retornar, una a la vez. Como solo hay una pila, solo se ejecuta una porción de código en cualquier momento. Si una función tarda mucho, nada más —ni siquiera el clic en un botón— puede ejecutarse hasta que termine. Por eso un bucle pesado «congela» la página.

Entonces, ¿cómo espera JavaScript un temporizador de 2 segundos o una descarga lenta sin bloquearlo todo? El truco es que no espera en absoluto. Delega esos trabajos al entorno que lo rodea y sigue adelante.

Es el entorno el que espera

El navegador (y Node.js) le da a JavaScript poderes adicionales que no forman parte del lenguaje en sí: temporizadores, peticiones de red, acceso a archivos, escuchadores de eventos. Cuando llamas a setTimeout o fetch, el motor delega esa tarea al entorno y avanza de inmediato. El entorno espera en segundo plano, y cuando el trabajo está hecho coloca tu callback en una cola (queue) — no directamente de vuelta en la pila.

Qué hace realmente el event loop

Aquí es donde entra el event loop. Su tarea es sencilla de enunciar: cada vez que la pila de llamadas está vacía, tomar el siguiente callback de la cola y apilarlo para ejecutarlo. Da vueltas indefinidamente, comprobando «¿está vacía la pila? ¿hay algo esperando?» y alimentando los callbacks en cola de uno en uno. Esa única regla es todo el mecanismo detrás del JavaScript asíncrono.

La consecuencia clave: tus callbacks en cola solo se ejecutan después de que todo el código síncrono actual haya terminado. Un setTimeout(fn, 0) no se ejecuta «ahora» — se ejecuta una vez que la pila queda libre, aunque sea más tarde de lo que esperabas.

Código JavaScript mostrado en una pantalla
El código síncrono se ejecuta primero hasta completarse; solo cuando la pila de llamadas se vacía el event loop empieza a inyectar los callbacks en cola.

Microtareas vs macrotareas: por qué ganan las promesas

No hay una sola cola sino (al menos) dos, y la diferencia explica el tropiezo más habitual en las entrevistas. Las macrotareas incluyen cosas como setTimeout y los callbacks de E/S. Las microtareas incluyen los callbacks de promesas resueltas (.then) y queueMicrotask. La regla es que después de cada macrotarea, el event loop vacía la cola de microtareas por completo antes de continuar.

Por eso esto imprime start, end, promise, timeout — la promesa se ejecuta antes que el temporizador aunque este se haya fijado a 0:

console.log('start');
setTimeout(() => console.log('timeout'), 0);
Promise.resolve().then(() => console.log('promise'));
console.log('end');

Las dos llamadas a console.log son síncronas y se ejecutan primero. El callback de la promesa es una microtarea y se ejecuta en cuanto la pila queda libre. El timeout es una macrotarea y espera su turno tras las microtareas.

Por qué importa en la práctica

El event loop explica los comportamientos que hacen tropezar a la gente: por qué setTimeout(fn, 0) significa «lo antes posible» y no «inmediatamente», por qué un cálculo síncrono largo deja toda la página sin responder, y por qué el código async/await se reanuda solo cuando el trabajo síncrono circundante ha terminado. Una vez que visualizas la pila vaciándose y el bucle inyectando los callbacks en cola, el JavaScript asíncrono deja de ser un misterio.

Node.js vs el navegador

El concepto es el mismo en Node.js, pero el anfitrión es distinto: en lugar de las Web APIs del navegador, Node usa una biblioteca llamada libuv para gestionar temporizadores, sistema de archivos y red fuera del hilo principal. El bucle de Node tiene algunas fases adicionales (y process.nextTick se sitúa incluso antes que las microtareas normales), pero el modelo mental es idéntico — un hilo, un entorno que espera y un bucle que reinyecta el trabajo terminado.

En resumen

El event loop es como el JavaScript de un solo hilo se mantiene responsivo: el código síncrono se ejecuta en la pila de llamadas, el trabajo lento se delega al entorno y los callbacks terminados esperan en colas hasta que la pila esté vacía. Las microtareas (promesas) se ejecutan antes que las macrotareas (temporizadores), y nada asíncrono interrumpe jamás un código que ya se está ejecutando. Mantén esa imagen y el resto del JavaScript asíncrono — promesas, async/await, temporizadores — encaja en su sitio.

Preguntas frecuentes

¿Qué es el event loop en JavaScript?
Es el mecanismo que permite que JavaScript, de un solo hilo, siga respondiendo: en cuanto la pila de llamadas está vacía, el event loop toma el siguiente callback en cola y lo ejecuta. El trabajo lento (temporizadores, peticiones de red) se delega al entorno, que pone tu callback en cola cuando termina.
¿Por qué una promesa se ejecuta antes que setTimeout?
Los callbacks de promesa son microtareas y los temporizadores son macrotareas. Después de cada macrotarea, el event loop vacía primero toda la cola de microtareas, así que el .then de una promesa resuelta se ejecuta antes que un callback setTimeout(…, 0), aunque el temporizador fuera cero.
¿setTimeout(fn, 0) se ejecuta inmediatamente?
No. Se ejecuta en cuanto la pila de llamadas está libre y después de las microtareas pendientes, no al instante. «0» significa «lo antes posible», que sigue siendo después de que termine el código síncrono actual.
¿El event loop es igual en Node.js y el navegador?
El modelo mental es el mismo — un solo hilo, el entorno espera y un bucle reinyecta los callbacks terminados. Node.js usa libuv y tiene algunas fases adicionales (y process.nextTick va antes que las microtareas normales), pero la idea central es idéntica.