Je pense que je peux illustrer cela très bien. Puisque nextTick
est appelé à la fin de l'opération en cours, l'appel récursif peut empêcher la boucle d'événement de continuer. setImmediate
ré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 setImmediate
de s'exécuter. Donc, idéalement, la plupart de ces appels seront en fait assez immédiats, mais pas aussi immédiats que nextTick
ce 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 setImmediate
et 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 step
fonction avec l'itération zéro. Il enregistrera ensuite deux gestionnaires, un pour setImmediate
et un pour process.nextTick
. Nous appelons ensuite récursivement cette fonction à partir du setImmediate
gestionnaire qui s'exécutera dans la prochaine phase de vérification. Le nextTick
gestionnaire 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 nextTick
dé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 setImmediate
déclenche et appelle récursivement notre step
fonction pour recommencer le processus. L'opération en cours se termine, se nextTick
dé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 step
dans notre nextTick
gestionnaire 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 step
dans le nextTick
gestionnaire, 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' step
enregistrement d'un setImmedaite
gestionnaire ainsi que d'un nextTick
gestionnaire. Une fois l'opération en cours terminée, notre nextTick
gestionnaire se déclenche step
et appelle et enregistre récursivement un autre setImmediate
gestionnaire ainsi qu'un autre nextTick
gestionnaire. Étant donné qu'un nextTick
gestionnaire se déclenche après l'opération en cours, l'inscription d'un nextTick
gestionnaire dans un nextTick
gestionnaire entraînera l'exécution du deuxième gestionnaire immédiatement après la fin de l'opération en cours. Les nextTick
gestionnaires continueront de tirer, empêchant la boucle d'événement actuelle de se poursuivre. Nous traverserons tous nosnextTick
avant de voir un seul setImmediate
incendie 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 nextTick
appels continueraient de se reproduire et ne laisseraient jamais la boucle d'événement passer à la phase suivante. C'est ainsi que le nextTick
blocage peut se produire lorsqu'il est utilisé de manière récursive alors qu'il setImmediate
se déclenche dans la prochaine boucle d'événements et la définition d'un autre setImmediate
gestionnaire à 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 nextTick
on 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.