Event Loop, Microtasks y Concurrencia Interna
Tiempo estimado de lectura: 4 min
- Microtasks tienen prioridad sobre macrotasks y render.
- Una cola de microtasks que se auto‑agenda puede provocar starvation.
- En Node hay matices: process.nextTick y setImmediate afectan el orden.
- Chunking o workers son la forma segura de manejar trabajo pesado.
Introducción
Event Loop, Microtasks y Concurrencia Interna: dominar esto no es solo saber usar async/await. En las primeras líneas: si no entiendes Call Stack, Web/Node APIs, Task Queue y Microtask Queue —y la prioridad que tienen— seguirás viendo servidores congelados y frontends con “jank”.
Resumen rápido (lectores con prisa)
El motor ejecuta macrotasks una por iteración y vacía la cola de microtasks tras cada macrotask. Las microtasks se ejecutan antes que render y macrotasks, y pueden provocar starvation si se auto‑agendan. En Node hay colas adicionales como process.nextTick y diferencias entre setImmediate y setTimeout.
Event Loop, Microtasks y Concurrencia Interna: qué debes dominar
- Call Stack: donde corre el código síncrono. Si algo ocupa la pila mucho tiempo, todo se bloquea.
- Web APIs / Node APIs: el entorno (browser, libuv) ejecuta I/O, timers y los devuelve como callbacks.
- Task Queue (Macrotasks):
setTimeout, I/O, eventos; se procesa una macrotask por iteración. - Microtask Queue:
Promise.then,queueMicrotask,MutationObserver(y en Nodeprocess.nextTickcon sus matices); se vacía completamente tras cada macrotask. - Prioridad: microtasks > macrotasks > render (en browsers).
- Starvation: microtasks recursivas pueden impedir que el loop avance.
- Node specifics:
setImmediatevssetTimeout,process.nextTickvsPromise.resolve.
Cómo funciona en la práctica (y por qué te importa)
Cada iteración del loop suele seguir este patrón:
- Ejecuta la macrotask en curso.
- Vacía la microtask queue completamente.
- (Browsers) Renderiza si es necesario.
- Coge la siguiente macrotask.
Consecuencia directa: si una microtask agenda otra microtask sin parar, el motor se queda en el paso 2 y nunca llega al render ni a otras macrotasks. Resultado: UI congelada o servidor que no responde.
Ejemplo de starvation (no lo hagas en producción):
function starve() {
Promise.resolve().then(starve);
}
starve();
Node.js: matices que importan
Node tiene fases internas; aquí las diferencias que debes aplicar:
- process.nextTick vs Promise.resolve
process.nextTicktiene su propia cola y se procesa antes que la cola de Promises. Úsalo para cleanup urgente, pero evita recursividad: unnextTickrecursivo bloquea I/O. - setImmediate vs setTimeout(…, 0)
Dentro de un callback de I/O,
setImmediatese ejecuta antes que timers. En el script principal el orden puede ser no determinista. Para tareas post‑I/O preferimossetImmediate.
Ejemplo ilustrativo:
fs.readFile(__filename, () => {
setTimeout(() => console.log('timeout-in-io'), 0);
setImmediate(() => console.log('immediate-in-io'));
});
// Salida consistente: immediate-in-io → timeout-in-io
Fuente: Node docs.
Reglas prácticas y patrones seguros
- Microtasks para consistencia, macrotasks para ceder control
Usa microtasks (
queueMicrotask,Promise.then) para garantizar orden lógico inmediato, no para procesar grandes volúmenes. - Chunking: divide trabajo pesado
Para arrays grandes o computación pesada, trocea la ejecución y programa cada chunk con macrotasks (
setTimeout/setImmediate) o usa Web Workers / Worker Threads para off‑main. - Evita
process.nextTicksin pensarEs potente pero peligroso; documenta su uso y limita su alcance.
- Usa herramientas de profiling
Chrome DevTools Performance para front;
clinic.js,--inspectpara Node. Busca long tasks y spikes en microtasks. - Prefiere APIs modernas cuando estén disponibles
scheduler.yield()orequestIdleCallback/requestAnimationFrameson mejores opciones en escenarios específicos (revisar compatibilidad).
Chunking ejemplo:
function processLarge(items, chunk = 200) {
let i = 0;
function next() {
const end = Math.min(i + chunk, items.length);
for (; i < end; i++) heavy(items[i]);
if (i < items.length) setTimeout(next, 0); // cede el hilo
}
next();
}
Criterio técnico resumido (para tu equipo)
- Define en code reviews: microtasks solo para cosas <10ms y que requieran orden inmediato.
- Chunking obligatorio para loops que procesen >1k elementos.
- Mueve CPU‑bound a workers.
- En Node:
setImmediatepost‑I/O,nextTicksolo para cleanup crítico.
Conclusión
Dominar Event Loop, Microtasks y Concurrencia Interna no es trivia: es diseño de sistemas. Entender quién tiene prioridad (microtasks) y cómo evitar starvation cambia aplicaciones que “funcionan” por aplicaciones que escalan y no frustran usuarios. Si quieres que tu equipo deje de parchear problemas de rendimiento, empieza por aquí.
FAQ
- ¿Qué diferencia hay entre microtasks y macrotasks?
- ¿Por qué las microtasks pueden provocar starvation?
- ¿Cuándo usar
process.nextTicken Node? - ¿Qué alternativa usar para tareas largas en el frontend?
- ¿Cómo diagnostico long tasks y spikes de microtasks?
- ¿Cuál es la regla práctica para chunking?
Las microtasks (ej. Promise.then) se ejecutan inmediatamente después de la macrotask en curso y antes del render; la cola se vacía por completo. Las macrotasks (ej. setTimeout, I/O) se procesan una por iteración del loop.
Porque la cola de microtasks se vacía completamente tras cada macrotask: si una microtask agenda otra microtask recurrentemente, el loop nunca avanza a render ni a nuevas macrotasks, congelando la UI o bloqueando I/O.
process.nextTick se usa para cleanup urgente que debe ejecutarse antes de otras promesas. Evítalo para trabajo repetitivo o no crítico, ya que su cola se procesa antes que la cola de Promises y puede bloquear I/O si se abusa.
Usar chunking con macrotasks (setTimeout, setImmediate) o delegar a Web Workers / Worker Threads para mover CPU‑bound fuera del hilo principal.
En frontend, Chrome DevTools → Performance para identificar long tasks. En Node, usar clinic.js y --inspect para perf profiles; buscar spikes y colas saturadas de microtasks.
Trocea procesamiento grande en chunks (ej. 200–1000 items) y programa la ejecución de cada chunk con macrotasks para ceder el hilo entre ellos. Chunking obligatorio para loops >1k según criterio técnico.

Leave a Reply