Node programa el trabajo asíncrono en dos familias de colas: microtareas (Promises, queueMicrotask, process.nextTick) y macrotareas (temporizadores, setImmediate). El orden de vaciado explica los errores de temporización sutiles.
1. Ejecutar la pila de llamadas hasta que esté vacía
2. Vaciar TODAS las devoluciones de llamada de process.nextTick
3. Vaciar TODAS las microtareas (Promises, queueMicrotask)
4. Ejecutar UNA macrotarea (temporizador, E/S, setImmediate, ...)
5. Repetir desde el paso 2
import { setTimeout, setImmediate } from 'node:timers';async function chain(): Promise<void> { console.log('A'); await Promise.resolve(); console.log('B');}chain();Promise.resolve().then(() => console.log('C'));process.nextTick(() => console.log('D'));setTimeout(() => console.log('E'), 0);console.log('F');// A, F, D, C, B, E (B puede intercalarse con C dependiendo de la temporización de await)
// Peligroso: nextTick recursivo agota la E/Sfunction spin(count: number): void { if (count <= 0) return; process.nextTick(() => spin(count - 1));}// spin(1_000_000); // Nunca hagas esto en producción
Lo que esto demuestra:
El código síncrono (A, F) se ejecuta antes que cualquier trabajo asíncrono en cola
nextTick precede a las microtareas de Promise
await divide las funciones asíncronas en continuaciones de microtareas
nextTick recursivo impide que se ejecuten las macrotareas (incluida la E/S)
// Aplazamiento seguro: no anula la cola de Promise como nextTickfunction defer(fn: () => void): void { queueMicrotask(fn);}// Se ejecuta después del lote de E/S actual, no antes de otras microtareasimport { setImmediate } from 'node:timers';function deferMacrotask(fn: () => void): void { setImmediate(fn);}
process.nextTick recursivo - agota los temporizadores y la E/S indefinidamente. Solución: usa setImmediate para el procesamiento por lotes o un worker.
Asumir que await hace que el código sea "paralelo" - solo produce microtareas en el mismo hilo. Solución: usa workers para el paralelismo de CPU.
Mezclar nextTick con cadenas de Promise para hacks de ordenación - frágil en diferentes versiones de Node. Solución: reestructura con máquinas de estado explícitas o colas.
Rechazos no manejados en microtareas - bloquean el proceso si no se capturan antes de la siguiente macrotarea. Solución: siempre try/catch alrededor de await en los manejadores de eventos.
Registrar el orden de las microtareas en producción - los errores de orden son problemas de tiempo de desarrollo; la producción necesita métricas. Solución: corrige la arquitectura, no dependas del orden de los registros.
Microtareas. Las continuaciones de .then, .catch, .finally y await utilizan la cola de microtareas.
¿Por qué existe nextTick si tenemos queueMicrotask?
API histórica que precede a las microtareas en Node. nextTick se ejecuta antes que las Promises; a veces se usa en los internos de los streams; evítalo en el código de la aplicación.
¿Cuántas microtareas se ejecutan por ciclo?
Todas ellas: la cola se vacía completamente antes de que comience la siguiente macrotarea.
¿setImmediate se ejecuta antes o después de las Promises?
Después: setImmediate es una macrotarea. Todas las microtareas pendientes se vacían primero.
¿Qué pasa con los valores de retorno de las funciones asíncronas?
Una función async siempre devuelve una Promise. El código después de await se programa como microtareas.
¿Pueden las microtareas causar un desbordamiento de pila?
Las cadenas de microtareas infinitas pueden agotar la memoria o agotar la E/S, un riesgo similar al de nextTick recursivo.
¿En qué se diferencian los navegadores?
Los navegadores carecen de process.nextTick y setImmediate. Las reglas de ordenación de setTimeout(0) vs Promise son similares.
¿queueMicrotask funciona en las devoluciones de llamada?
Sí, se programa en la cola de microtareas para la iteración actual del bucle, después de que la pila de llamadas actual se vacíe.
¿Cómo pruebo la ordenación?
Escribe pruebas de ordenación explícitas con node:test y arrays de registro, pero prefiere eliminar las dependencias de orden en el código de la aplicación.
¿Qué se ejecuta después de await fetch?
Continuaciones de microtareas después de que la Promise se resuelva, luego más macrotareas para la E/S de socket subyacente.
¿MutationObserver es relevante en Node?
No, esa es una API de navegador. Las microtareas de Node provienen de Promises y queueMicrotask.
¿Cuándo deben las librerías usar nextTick?
Raramente; los internos heredados de los streams lo usaban por compatibilidad. El código de la aplicación debe usar queueMicrotask o setImmediate.