Eu acho que posso ilustrar isso muito bem. Como nextTické chamado no final da operação atual, chamá-lo recursivamente pode acabar impedindo a continuidade do loop de eventos. setImmediateresolve isso disparando na fase de verificação do loop de eventos, permitindo que o loop de eventos continue normalmente.
┌───────────────────────┐
┌─>│ timers │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ I/O callbacks │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ idle, prepare │
│ └──────────┬────────────┘ ┌───────────────┐
│ ┌──────────┴────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └──────────┬────────────┘ │ data, etc. │
│ ┌──────────┴────────────┐ └───────────────┘
│ │ check │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
└──┤ close callbacks │
└───────────────────────┘
fonte: https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/
Observe que a fase de verificação ocorre imediatamente após a fase de votação. Isso ocorre porque a fase de pesquisa e os retornos de chamada de E / S são os locais mais prováveis para os quais suas chamadas setImmediateserão executadas. Portanto, idealmente, a maioria dessas chamadas será realmente imediata, mas não tão imediata quanto a nextTickque é verificada após cada operação e existe tecnicamente fora do loop de eventos.
Vamos dar uma olhada em um pequeno exemplo da diferença entre setImmediatee 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 executar este programa e estamos avançando na primeira iteração do loop de eventos. Ele chamará a stepfunção com a iteração zero. Em seguida, ele registrará dois manipuladores, um para setImmediatee outro para process.nextTick. Em seguida, chamamos recursivamente essa função do setImmediatemanipulador que será executado na próxima fase de verificação. O nextTickmanipulador será executado no final da operação atual, interrompendo o loop de eventos; portanto, mesmo que tenha sido registrado em segundo, ele será executado primeiro.
A ordem acaba sendo: nextTickdispara quando a operação atual termina, o próximo loop de evento começa, as fases normais do loop de evento são executadas, setImmediatedispara e chama recursivamente nossa stepfunção para iniciar o processo novamente. A operação atual termina, nextTickdispara, etc.
A saída do código acima seria:
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
Agora vamos passar nossa chamada recursiva para stepnosso nextTickmanipulador, em vez de 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);
Agora que transferimos a chamada recursiva para stepo nextTickmanipulador, as coisas se comportarão em uma ordem diferente. Nossa primeira iteração do loop de eventos é executada e chama o stepregistro de um setImmedaitemanipulador e de um nextTickmanipulador. Após o término da operação atual, nosso nextTickmanipulador é acionado, o que chama stepe registra recursivamente outro setImmediatemanipulador e outro nextTickmanipulador. Como um nextTickmanipulador é acionado após a operação atual, o registro de um nextTickmanipulador em um nextTickmanipulador fará com que o segundo manipulador seja executado imediatamente após o término da operação do manipulador atual. Os nextTickmanipuladores continuarão disparando, impedindo que o loop de eventos atual continue. Vamos passar por todo o nossonextTickmanipuladores antes de vermos um único setImmediateacionador disparar.
A saída do código acima acaba sendo:
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
Observe que, se não tivéssemos interrompido a chamada recursiva e a abortado após 10 iterações, as nextTickchamadas continuariam recorrendo e nunca deixando o loop de eventos continuar para a próxima fase. É assim que nextTickpode ser bloqueado quando usado recursivamente, enquanto setImmediatedispara no próximo loop de eventos e definir outro setImmediatemanipulador de dentro de um não interrompe o loop de eventos atual, permitindo que continue executando as fases do loop de eventos normalmente.
Espero que ajude!
PS - Eu concordo com outros comentaristas que os nomes das duas funções poderiam ser facilmente trocados, pois nextTickparece que isso será acionado no próximo loop de eventos, em vez do final do atual, e o final do atual é mais "imediato" "que o início do próximo loop. Bem, é isso que obtemos à medida que a API amadurece e as pessoas dependem das interfaces existentes.