Creo que puedo ilustrar esto bastante bien. Como nextTick
se llama al final de la operación actual, llamarla de forma recursiva puede terminar bloqueando la continuación del bucle de eventos. setImmediate
resuelve esto disparando en la fase de verificación del bucle de eventos, permitiendo que el bucle de eventos continúe normalmente.
┌───────────────────────┐
┌─>│ timers │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ I/O callbacks │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ idle, prepare │
│ └──────────┬────────────┘ ┌───────────────┐
│ ┌──────────┴────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └──────────┬────────────┘ │ data, etc. │
│ ┌──────────┴────────────┐ └───────────────┘
│ │ check │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
└──┤ close callbacks │
└───────────────────────┘
fuente: https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/
Observe que la fase de verificación es inmediatamente posterior a la fase de sondeo. Esto se debe a que la fase de sondeo y las devoluciones de llamadas de E / S son los lugares más probables en los que setImmediate
se ejecutarán sus llamadas . Así que, idealmente, la mayoría de esas llamadas serán bastante inmediatas, pero no tan inmediatas como las nextTick
que se verifican después de cada operación y técnicamente existen fuera del bucle de eventos.
Echemos un vistazo a un pequeño ejemplo de la diferencia entre setImmediate
y process.nextTick
:
function step(iteration) {
if (iteration === 10) return;
setImmediate(() => {
console.log(`setImmediate iteration: ${iteration}`);
step(iteration + 1); // Recursive call from setImmediate handler.
});
process.nextTick(() => {
console.log(`nextTick iteration: ${iteration}`);
});
}
step(0);
Digamos que acabamos de ejecutar este programa y estamos pasando por la primera iteración del bucle de eventos. Llamará a la step
función con la iteración cero. Luego registrará dos controladores, uno para setImmediate
y otro para process.nextTick
. Luego llamamos recursivamente a esta función desde el setImmediate
controlador que se ejecutará en la siguiente fase de verificación. El nextTick
controlador se ejecutará al final de la operación actual interrumpiendo el bucle de eventos, por lo que, aunque se registró en segundo lugar, se ejecutará primero.
El orden termina siendo: nextTick
dispara a medida que finaliza la operación actual, comienza el siguiente bucle de eventos, se ejecutan las fases normales del bucle de eventos, se setImmediate
dispara y recursivamente llama a nuestra step
función para comenzar el proceso nuevamente. Finaliza la operación actual, nextTick
incendios, etc.
La salida del código anterior sería:
nextTick iteration: 0
setImmediate iteration: 0
nextTick iteration: 1
setImmediate iteration: 1
nextTick iteration: 2
setImmediate iteration: 2
nextTick iteration: 3
setImmediate iteration: 3
nextTick iteration: 4
setImmediate iteration: 4
nextTick iteration: 5
setImmediate iteration: 5
nextTick iteration: 6
setImmediate iteration: 6
nextTick iteration: 7
setImmediate iteration: 7
nextTick iteration: 8
setImmediate iteration: 8
nextTick iteration: 9
setImmediate iteration: 9
Ahora pasemos nuestra llamada recursiva a step
nuestro nextTick
controlador en lugar del setImmediate
.
function step(iteration) {
if (iteration === 10) return;
setImmediate(() => {
console.log(`setImmediate iteration: ${iteration}`);
});
process.nextTick(() => {
console.log(`nextTick iteration: ${iteration}`);
step(iteration + 1); // Recursive call from nextTick handler.
});
}
step(0);
Ahora que se ha pasado la llamada recursiva a step
en las nextTick
cosas del manejador se comportará en un orden diferente. Nuestra primera iteración del bucle de eventos se ejecuta y llama al step
registro de un setImmedaite
controlador y un nextTick
controlador. Después de que finaliza la operación actual, nuestro nextTick
controlador se activa step
y llama y registra recursivamente a otro setImmediate
controlador, así como a otro nextTick
controlador. Dado que un nextTick
controlador se dispara después de la operación actual, registrar un nextTick
controlador dentro de un nextTick
controlador hará que el segundo controlador se ejecute inmediatamente después de que finalice la operación del controlador actual. Los nextTick
controladores seguirán disparando, evitando que el ciclo de eventos actual continúe. Vamos a superar todos nuestrosnextTick
manejadores antes de que veamos un solo setImmediate
manejo de fuego.
La salida del código anterior termina siendo:
nextTick iteration: 0
nextTick iteration: 1
nextTick iteration: 2
nextTick iteration: 3
nextTick iteration: 4
nextTick iteration: 5
nextTick iteration: 6
nextTick iteration: 7
nextTick iteration: 8
nextTick iteration: 9
setImmediate iteration: 0
setImmediate iteration: 1
setImmediate iteration: 2
setImmediate iteration: 3
setImmediate iteration: 4
setImmediate iteration: 5
setImmediate iteration: 6
setImmediate iteration: 7
setImmediate iteration: 8
setImmediate iteration: 9
Tenga en cuenta que si no hubiéramos interrumpido la llamada recursiva y la hubiéramos abortado después de 10 iteraciones, las nextTick
llamadas seguirían recurriendo y nunca dejarían que el bucle de eventos continuara a la siguiente fase. Así es como nextTick
puede convertirse en bloqueo cuando se usa de forma recursiva, mientras que setImmediate
se disparará en el próximo ciclo de eventos y establecer otro setImmediate
controlador desde dentro no interrumpirá en absoluto el ciclo de eventos actual, lo que le permitirá continuar ejecutando fases del ciclo de eventos de manera normal.
¡Espero que ayude!
PD: estoy de acuerdo con otros comentaristas en que los nombres de las dos funciones podrían intercambiarse fácilmente, ya que nextTick
parece que se activará en el siguiente bucle de eventos en lugar del final del actual, y el final del bucle actual es más "inmediato" "que el comienzo del siguiente ciclo. Bueno, eso es lo que obtenemos a medida que una API madura y la gente depende de las interfaces existentes.