El Event Loop de JavaScript

Cómo un solo hilo ejecuta código "concurrente"

  • 1 Conceptos: motor de JS y runtime
  • 2 Conceptos: sync/async y callbacks
  • 3 Conceptos: promesas y Event Loop
  • 4 Código sincrónico
  • 5 Trabajo pesado (bloqueo)
  • 6 Prioridad: micro vs task
  • 7 UI Events + Todo junto
  • 8 Microtareas no repintan entre sí
  • 9 fetch + I/O de red
  • 10 fetch concurrente + epoll
  • 11 async / await (caso real)
  • 12 Polling de servidor (setInterval + fetch)

Usa ← → para navegar · Espacio = siguiente paso · R = reiniciar

El Motor de JS y el Runtime

Single-threaded

El motor tiene un único Call Stack: solo puede ejecutar una función a la vez, de forma sincrónica. No hay dos líneas de tu JS corriendo en paralelo.

Call Stack pila de llamadas

Cada vez que se llama a una función se apila un "frame"; cuando termina, se desapila. Si el stack nunca se vacía (loop infinito, recursión sin fin), el hilo se congela.

Runtime

Es el "todo": motor JS + Web APIs (o APIs de Node) + colas de callbacks + Event Loop. El navegador es un runtime; Node.js es otro runtime (con libuv en vez de las Web APIs del browser).

Sincronía, Asincronía y Callbacks

Síncrono vs asíncrono

Síncrono: cada línea espera a que termine la anterior. Asíncrono: se dispara una operación (timer, red, archivo) y el resultado llega después, sin bloquear el hilo mientras se espera.

Callback

Una función que se pasa como argumento para ejecutarse más adelante, cuando algo termine (setTimeout(fn, 1000), btn.addEventListener('click', fn)). La forma más primitiva de manejar asincronía en JS.

Callback hell

Anidar callbacks dentro de callbacks para encadenar pasos asíncronos vuelve el código difícil de leer y mantener. Las promesas nacieron para resolver este problema.

Promesas, async/await y Event Loop

Promesa Promise

Un objeto que representa un valor que todavía no existe pero existirá (o fallará) en el futuro. 3 estados: pending, fulfilled y rejected. Se consume con .then() / .catch() o con await.

async / await

Azúcar sintáctica sobre promesas. Una función async siempre devuelve una promesa; await pausa esa función (sin bloquear el hilo) hasta que la promesa se resuelve, y el código se lee como si fuera sincrónico.

Event Loop

El mecanismo que coordina todo: mientras el Call Stack esté vacío, va tomando callbacks pendientes (timers, promesas, eventos de UI, I/O) y los ejecuta uno por uno. Permite que un lenguaje de un solo hilo maneje miles de operaciones "al mismo tiempo".

Conclusiones Clave

  • El ciclo del Event Loop (orden real, spec WHATWG): ① ejecutar UNA macrotask (un click de la UI Event Queue, o un timer de la Task Queue) · ② drenar TODAS las microtareas (Promise, queueMicrotask…) · ③ render: estilo/layout/paint + requestAnimationFrame (~60fps, puede saltearse) → repetir. Por eso el handler de un click corre ANTES del repintado: el render va al final de la vuelta.
  • Call Stack — JS es single-threaded. Un solo Call Stack, una función a la vez.
  • Web APIssetTimeout, fetch, rAF y los listeners NO los maneja el motor JS: viven en el navegador (C++/OS), trabajan de forma concurrente y depositan su callback en la cola correspondiente al terminar.
  • Macrotasks: UI Event Queue + Task Queue — eventos de UI (click, input) y timers (setTimeout, setInterval) son macrotasks de task sources distintas. Se consume una por vuelta.
  • Microtask QueuePromise.then, queueMicrotask, await. Se drena completa después de CADA macrotask, antes del render.
  • async / await = promesas — cada await pausa la función y libera el hilo; lo que sigue se reanuda como microtarea. Incluso res.json() es async: encola otra microtarea.
  • Escribir el DOM es síncronoinnerHTML, appendChild cambian el DOM al instante; lo que se difiere al render es el recálculo de estilo/layout y el repintado.
  • Bloqueo vs fetch + epoll — un loop largo congela render y UI (usa Web Workers). En cambio el hilo nunca espera la red: delega al OS (epoll/kqueue/IOCP) y la respuesta vuelve como microtarea.
Fuentes
· HTML Standard — Event loop processing model (WHATWG): el orden tarea → microtasks → "update the rendering"
· Jake Archibald — Tasks, microtasks, queues and schedules (jakearchibald.com): el render ocurre entre tareas, las microtasks antes del paint
· MDN — Microtask guide / In depth: las microtasks se drenan completas tras cada tarea