Cómo un solo hilo ejecuta código "concurrente"
Usa ← → para navegar · Espacio = siguiente paso · R = reiniciar
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.
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.
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).
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.
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.
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.
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.
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.
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".
requestAnimationFrame (~60fps,
puede saltearse) → repetir.
Por eso el handler de un click corre ANTES del repintado: el
render va al final de la vuelta.
setTimeout,
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.
click, input) y timers
(setTimeout, setInterval) son macrotasks
de task sources distintas. Se consume una por
vuelta.
Promise.then,
queueMicrotask, await. Se drena
completa después de CADA macrotask, antes del render.
await pausa la función y libera el hilo; lo que sigue
se reanuda como microtarea. Incluso
res.json() es async: encola otra microtarea.
innerHTML, appendChild cambian el DOM al
instante; lo que se difiere al render es el recálculo de
estilo/layout y el repintado.