Bucle de eventos: cómo JavaScript gestiona la asincronía
Al finalizar este tema
Comprenderá el principio por el cual JavaScript, siendo de un solo hilo, puede gestionar operaciones asíncronas.
¿Cómo es posible si es de un solo hilo?
JavaScript ejecuta una sola tarea a la vez. Es de un solo hilo. Sin embargo, gestiona simultáneamente temporizadores, peticiones de red y eventos de clic. ¿Cómo lo hace?
El secreto está en el bucle de eventos (Event Loop). El motor de JavaScript (V8) no lo hace solo; el navegador (o Node.js) le ayuda.
Tres componentes clave
Pila de llamadas (Call Stack): Es donde se apilan las funciones que se están ejecutando actualmente. Cuando se llama a una función, se añade a la pila, y cuando termina su ejecución, se elimina. Tiene una estructura LIFO (Last In, First Out).
Web API: Son las funcionalidades proporcionadas por el navegador, como setTimeout, fetch y los escuchadores de eventos del DOM. Funcionan fuera del motor de JavaScript.
Cola de tareas (Task Queue): Es la fila donde esperan los callbacks completados por las Web API. Cuando la pila de llamadas está vacía, se extraen de aquí y se ejecutan uno a uno.
Orden de ejecución
console.log("1");
setTimeout(() => {
console.log("2");
}, 0);
console.log("3");Salida: 1, 3, 2.
Aunque es un temporizador de 0 ms, el "2" es el último en aparecer. Siguiendo el flujo:
console.log("1")→ se ejecuta en la pila de llamadas (call stack) → salidasetTimeout→ se registra el temporizador en la Web API → sale de la pila de llamadasconsole.log("3")→ se ejecuta en la pila de llamadas → salida- La pila de llamadas está vacía → el bucle de eventos (event loop) comprueba la cola de tareas → se ejecuta el callback → se imprime "2"
setTimeout(fn, 0) no significa "en este preciso instante", sino "cuando la pila de llamadas esté vacía".
Cola de microtareas
El .then() de Promise no va a la cola de tareas, sino a la cola de microtareas (Microtask Queue). Las microtareas se ejecutan antes que las tareas.
console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
console.log("4");Salida: 1, 4, 3, 2.
Cuando la pila de llamadas (call stack) está vacía, el bucle de eventos (event loop):
- Primero vacía por completo la cola de microtareas (callbacks de Promise).
- Luego extrae una tarea de la cola de tareas (callback de setTimeout).
async/await también utiliza Promises internamente, por lo que es una microtarea.
Si la pila de llamadas se ocupa durante mucho tiempo
while (true) {
// bucle infinito
}Si la pila de llamadas (call stack) no está vacía, el bucle de eventos no se ejecuta. No habrá respuesta a los clics ni se ejecutarán los temporizadores. El navegador mostrará el mensaje "Esta página no responde".
Si necesitas realizar cálculos intensivos, utiliza Web Worker o divide la tarea para vaciar la pila de llamadas periódicamente.
Conceptos clave
JavaScript es monohilo, pero gestiona la asincronía mediante las Web API del navegador y el bucle de eventos.
setTimeout(fn, 0)significa "se ejecuta cuando la pila de llamadas está vacía". Las microtareas (Promise) siempre se ejecutan antes que las tareas (setTimeout).