Back

Cómo Funcionan las Promesas de JavaScript con el Event Loop

Cómo Funcionan las Promesas de JavaScript con el Event Loop

El comportamiento asíncrono de JavaScript a menudo confunde a los desarrolladores. Escribes setTimeout(() => console.log('timeout'), 0) esperando una ejecución inmediata, pero una Promesa se resuelve primero. Comprender cómo las Promesas de JavaScript interactúan con el Event Loop revela por qué sucede esto y te ayuda a escribir código asíncrono más predecible.

Puntos Clave

  • El event loop procesa todas las microtareas antes de pasar a la siguiente macrotarea
  • Las promesas y async/await utilizan la cola de microtareas, obteniendo prioridad sobre setTimeout
  • Comprender el sistema de dos colas ayuda a predecir el orden de ejecución de JavaScript
  • Las microtareas recursivas pueden saturar el event loop y bloquear las macrotareas

Los Fundamentos del Event Loop de JavaScript

JavaScript se ejecuta en un único hilo, procesando una operación a la vez. El event loop habilita operaciones asíncronas coordinando entre la pila de llamadas (call stack) y dos colas distintas: la cola de microtareas y la cola de macrotareas.

La pila de llamadas ejecuta código síncrono inmediatamente. Cuando la pila se vacía, el event loop verifica las tareas pendientes en un orden específico:

  1. Todas las microtareas se ejecutan primero
  2. Se ejecuta una macrotarea
  3. El ciclo se repite

Este sistema de prioridades explica por qué las promesas se comportan de manera diferente a setTimeout.

Macrotarea vs Microtarea en JavaScript: La Diferencia Crítica

Comprender las distinciones entre macrotareas y microtareas es esencial para predecir el orden de ejecución del código.

Las macrotareas incluyen:

  • setTimeout
  • setInterval
  • Operaciones de I/O
  • Renderizado de UI

Las microtareas incluyen:

  • Callbacks de promesas (.then, .catch, .finally)
  • queueMicrotask()
  • Callbacks de MutationObserver

El event loop procesa todas las microtareas antes de pasar a la siguiente macrotarea. Esto crea un sistema de prioridades donde las promesas siempre se ejecutan antes que los temporizadores.

Promesas y la Cola de Microtareas en Acción

Examinemos cómo las promesas interactúan con el event loop a través del código:

console.log('1');

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

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

console.log('5');

Salida: 1, 5, 3, 4, 2

Este es el flujo de ejecución:

  1. El console.log('1') síncrono se ejecuta inmediatamente
  2. setTimeout programa el callback en la cola de macrotareas
  3. Los callbacks de la promesa se encolan como microtareas
  4. El console.log('5') síncrono se ejecuta
  5. El event loop procesa todas las microtareas (3, 4)
  6. El event loop procesa una macrotarea (2)

La cola de microtareas se vacía completamente antes de que se ejecute cualquier macrotarea, incluso con temporizadores de retardo cero.

Integración de Async/Await y el Event Loop

El comportamiento de async/await sigue las mismas reglas de microtareas. La palabra clave await pausa la ejecución de la función y programa la continuación como una microtarea:

async function example() {
  console.log('1');
  await Promise.resolve();
  console.log('2');  // Esto se convierte en una microtarea
}

example();
console.log('3');

// Salida: 1, 3, 2

Después de await, el cuerpo restante de la función se une a la cola de microtareas. Esto explica por qué console.log('3') se ejecuta antes que console.log('2') a pesar de aparecer más tarde en el código.

Errores Comunes y Patrones Prácticos

Saturación de la Cola de Microtareas

Crear microtareas de forma recursiva puede bloquear el event loop:

function dangerousLoop() {
  Promise.resolve().then(dangerousLoop);
}
// No hagas esto - bloquea todas las macrotareas

Mezclar Temporizadores con Promesas

Al combinar diferentes patrones asíncronos, recuerda la prioridad de ejecución:

setTimeout(() => console.log('timeout'), 0);

fetch('/api/data')
  .then(response => response.json())
  .then(data => console.log('data'));

Promise.resolve().then(() => console.log('immediate'));

// Orden: immediate → data (cuando esté listo) → timeout

Depurar el Orden de Ejecución

Usa este patrón para rastrear el flujo de ejecución:

console.log('Sync start');

queueMicrotask(() => console.log('Microtask 1'));

setTimeout(() => console.log('Macrotask 1'), 0);

Promise.resolve()
  .then(() => console.log('Microtask 2'))
  .then(() => console.log('Microtask 3'));

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

console.log('Sync end');

// Salida: Sync start, Sync end, Microtask 1, Microtask 2, Microtask 3, Macrotask 1, Macrotask 2

Conclusión

El sistema de dos colas del event loop determina el orden de ejecución asíncrona de JavaScript. Las promesas y async/await utilizan la cola de microtareas, obteniendo prioridad sobre setTimeout y otras macrotareas. Este conocimiento transforma el comportamiento asíncrono misterioso en patrones predecibles, permitiéndote escribir código JavaScript asíncrono más confiable.

Recuerda: el código síncrono se ejecuta primero, luego se limpian todas las microtareas, después se ejecuta una macrotarea. Este ciclo se repite, permitiendo que el único hilo de JavaScript maneje operaciones asíncronas complejas de manera eficiente.

Preguntas Frecuentes

Los callbacks de las promesas son microtareas mientras que setTimeout crea macrotareas. El event loop siempre procesa todas las microtareas antes de pasar a la siguiente macrotarea, independientemente de la duración del timeout.

Sí, crear promesas o microtareas continuamente sin permitir que las macrotareas se ejecuten puede saturar el event loop. Esto impide que las actualizaciones de UI y otras macrotareas se ejecuten.

Async/await es azúcar sintáctico para las promesas. La palabra clave await pausa la ejecución y programa la continuación como una microtarea, siguiendo las mismas reglas de prioridad que los callbacks de promesas.

Los diferentes patrones siguen las reglas de sus colas. Las promesas y async/await usan microtareas, mientras que setTimeout y setInterval usan macrotareas. Las microtareas siempre se ejecutan primero cuando la pila de llamadas está vacía.

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay