Je pense que je peux illustrer cela très bien. Puisque nextTickest appelé à la fin de l'opération en cours, l'appel récursif peut empêcher la boucle d'événement de continuer. setImmediaterésout cela en tirant dans la phase de vérification de la boucle d'événements, permettant à la boucle d'événements de continuer normalement.
┌───────────────────────┐
┌─>│ timers │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ I/O callbacks │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ idle, prepare │
│ └──────────┬────────────┘ ┌───────────────┐
│ ┌──────────┴────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └──────────┬────────────┘ │ data, etc. │
│ ┌──────────┴────────────┐ └───────────────┘
│ │ check │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
└──┤ close callbacks │
└───────────────────────┘
source: https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/
Notez que la phase de vérification est immédiatement après la phase d'interrogation. En effet, la phase d'interrogation et les rappels d'E / S sont les endroits les plus susceptibles setImmediatede s'exécuter. Donc, idéalement, la plupart de ces appels seront en fait assez immédiats, mais pas aussi immédiats que nextTickce qui est vérifié après chaque opération et existe techniquement en dehors de la boucle d'événements.
Jetons un coup d'œil à un petit exemple de la différence entre setImmediateet 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);
Supposons que nous venons d'exécuter ce programme et que nous procédions à la première itération de la boucle d'événements. Il appellera la stepfonction avec l'itération zéro. Il enregistrera ensuite deux gestionnaires, un pour setImmediateet un pour process.nextTick. Nous appelons ensuite récursivement cette fonction à partir du setImmediategestionnaire qui s'exécutera dans la prochaine phase de vérification. Le nextTickgestionnaire s'exécutera à la fin de l'opération en cours interrompant la boucle d'événements, donc même s'il a été enregistré en second, il s'exécutera en premier.
L'ordre finit par être: se nextTickdéclenche à la fin de l'opération en cours, la prochaine boucle d'événement commence, les phases normales de la boucle d'événement s'exécutent, se setImmediatedéclenche et appelle récursivement notre stepfonction pour recommencer le processus. L'opération en cours se termine, se nextTickdéclenche, etc.
La sortie du code ci-dessus serait:
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
Passons maintenant à notre appel récursif stepdans notre nextTickgestionnaire au lieu 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);
Maintenant que nous avons déplacé l'appel récursif stepdans le nextTickgestionnaire, les choses se comporteront dans un ordre différent. Notre première itération de la boucle d'événements s'exécute et appelle l' stepenregistrement d'un setImmedaitegestionnaire ainsi que d'un nextTickgestionnaire. Une fois l'opération en cours terminée, notre nextTickgestionnaire se déclenche stepet appelle et enregistre récursivement un autre setImmediategestionnaire ainsi qu'un autre nextTickgestionnaire. Étant donné qu'un nextTickgestionnaire se déclenche après l'opération en cours, l'inscription d'un nextTickgestionnaire dans un nextTickgestionnaire entraînera l'exécution du deuxième gestionnaire immédiatement après la fin de l'opération en cours. Les nextTickgestionnaires continueront de tirer, empêchant la boucle d'événement actuelle de se poursuivre. Nous traverserons tous nosnextTickavant de voir un seul setImmediateincendie de gestionnaire.
La sortie du code ci-dessus finit par être:
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
Notez que si nous n'avions pas interrompu l'appel récursif et l'avions abandonné après 10 itérations, les nextTickappels continueraient de se reproduire et ne laisseraient jamais la boucle d'événement passer à la phase suivante. C'est ainsi que le nextTickblocage peut se produire lorsqu'il est utilisé de manière récursive alors qu'il setImmediatese déclenche dans la prochaine boucle d'événements et la définition d'un autre setImmediategestionnaire à l'intérieur de celui-ci n'interrompt pas du tout la boucle d'événements actuelle, ce qui lui permet de continuer à exécuter les phases de la boucle d'événements comme d'habitude.
J'espère que cela pourra aider!
PS - Je suis d'accord avec d'autres commentateurs sur le fait que les noms des deux fonctions pourraient facilement être échangés car nextTickon dirait que ça va se déclencher dans la prochaine boucle d'événement plutôt que la fin de la boucle actuelle, et la fin de la boucle actuelle est plus "immédiate" "que le début de la boucle suivante. Eh bien, c'est ce que nous obtenons à mesure qu'une API arrive à maturité et que les gens dépendent des interfaces existantes.