Skip to content

L57 - Microtasks vs Macrotareas

No todas las tareas asíncronas son iguales. JavaScript tiene dos colas de tareas con diferente prioridad:

  • Microtasks (Microtareas): tienen prioridad. Se ejecutan justo después de que el Call Stack se vacíe, antes de cualquier macrotarea.
  • Macrotasks (Macrotareas): se ejecutan en el siguiente ciclo del Event Loop, después de que se hayan procesado todas las microtasks disponibles.
FuenteEjemplo
Promesas.then(), .catch(), .finally()
queueMicrotask()Función explícita para encolar microtasks
MutationObserverObservar cambios en el DOM
process.nextTick()Solo en Node.js (se ejecuta antes que otras microtasks)
FuenteEjemplo
setTimeout()Timer con retardo
setInterval()Timer repetitivo
setImmediate()Solo en Node.js
I/OLectura/escritura de archivos, sockets
UI Eventsclick, keydown, mousemove…
requestAnimationFrameAntes del renderizado (técnicamente es una tarea especial)

El Event Loop sigue este orden:

  1. Ejecuta todo el código síncrono (Call Stack).
  2. Vacía todas las microtasks (cola de microtareas).
  3. Toma una macrotarea de la cola de macrotareas y la ejecuta.
  4. Vuelve al paso 2.

⚠️ Las microtasks se procesan hasta vaciar la cola antes de pasar a la siguiente macrotarea. Si una microtask encola otra microtask, esta también se ejecutará antes de la siguiente macrotarea.

🎯 Ejemplo clásico: Promise vs setTimeout

Section titled “🎯 Ejemplo clásico: Promise vs setTimeout”
console.log('1 - síncrono');
setTimeout(() => {
console.log('2 - macrotask (setTimeout)');
}, 0);
Promise.resolve().then(() => {
console.log('3 - microtask (Promise)');
});
console.log('4 - síncrono');
1 - síncrono
4 - síncrono
3 - microtask (Promise)
2 - macrotask (setTimeout)
  1. Se ejecuta console.log('1') (síncrono).
  2. setTimeout(cb, 0) registra el callback como macrotarea en la Web API.
  3. La Promise se resuelve con Promise.resolve() y su .then() se encola como microtask.
  4. Se ejecuta console.log('4') (síncrono).
  5. Call Stack vacío. Se procesan las microtasks: se ejecuta el .then() → imprime ‘3’.
  6. Se procesa la siguiente macrotarea: se ejecuta el setTimeout → imprime ‘2’.
console.log('A');
setTimeout(() => {
console.log('B (macrotask)');
Promise.resolve().then(() => {
console.log('C (microtask dentro de macrotask)');
});
}, 0);
Promise.resolve().then(() => {
console.log('D (microtask)');
setTimeout(() => {
console.log('E (macrotask dentro de microtask)');
}, 0);
});
console.log('F');
// Resultado:
// A
// F
// D (microtask)
// B (macrotask)
// C (microtask dentro de B)
// E (macrotask dentro de D → se encola después de B)

Si una microtask encola otra microtask y así sucesivamente, el Event Loop nunca llegará a las macrotareas, bloqueando el renderizado:

function loop() {
Promise.resolve().then(loop);
}
loop(); // ⚠️ Esto bloquea el navegador
// El Call Stack nunca se vacía completamente

Forma explícita de encolar una microtask:

queueMicrotask(() => {
console.log('Esto es una microtask');
});
// Aplazar cambios en el DOM hasta que se hayan procesado datos
datosActualizados = true;
// Usamos una microtask para asegurarnos de que cualquier código síncrono pendiente termine
queueMicrotask(() => {
if (datosActualizados) {
actualizarUI();
}
});
Síncrono (Call Stack)
Microtasks (Promesas, queueMicrotask) ← PRIORIDAD
Macrotasks (setTimeout, eventos I/O)
Renderizado del navegador

🎯 Regla de oro: las Promesas se resuelven antes que los setTimeout, aunque el delay sea 0. Siempre.