Ich denke, ich kann dies recht gut veranschaulichen. Da nextTick
am Ende des aktuellen Vorgangs aufgerufen wird, kann ein rekursiver Aufruf dazu führen, dass die Ereignisschleife blockiert wird. setImmediate
löst dieses Problem, indem es in der Überprüfungsphase der Ereignisschleife feuert, wodurch die Ereignisschleife normal fortgesetzt werden kann.
> timers
I/O-Rückrufe
Leerlauf, Vorbereitung
eingehend:
Umfrage < Verbindungen,
Daten usw.
Überprüfung
Schließen Rückrufe
Quelle: https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/
Beachten Sie, dass die Überprüfungsphase unmittelbar nach der Umfragephase liegt. Dies liegt daran, dass die Umfragephase und die I/O-Rückrufe die wahrscheinlichsten Stellen sind, an denen Ihre Aufrufe von setImmediate
ausgeführt werden. Idealerweise werden die meisten dieser Aufrufe tatsächlich ziemlich unmittelbar sein, nur nicht so unmittelbar wie nextTick
, der nach jedem Vorgang überprüft wird und technisch gesehen außerhalb der Ereignisschleife existiert.
Werfen wir einen Blick auf ein kleines Beispiel für den Unterschied zwischen setImmediate
und process.nextTick
:
function Schritt(Iteration) {
if (Iteration === 10) return;
setImmediate(() => {
console.log(`setImmediate Iteration: ${Iteration}`);
Schritt(Iteration + 1); // Rekursiver Aufruf vom setImmediate-Handler.
});
process.nextTick(() => {
console.log(`nextTick Iteration: ${Iteration}`);
});
}
Schritt(0);
Angenommen, wir haben dieses Programm gerade ausgeführt und befassen uns mit der ersten Iteration der Ereignisschleife. Es ruft die Funktion Schritt
mit Iteration null auf. Es registriert dann zwei Handler, einen für setImmediate
und einen für process.nextTick
. Wir rufen diese Funktion dann rekursiv vom setImmediate
-Handler auf, der in der nächsten Überprüfungsphase ausgeführt wird. Der nextTick
-Handler wird am Ende des aktuellen Vorgangs ausgeführt, unterbricht die Ereignisschleife und wird daher, obwohl er als zweites registriert wurde, tatsächlich als Erstes ausgeführt.
Die Reihenfolge lautet wie folgt: nextTick
wird am Ende des aktuellen Betriebs ausgelöst, die nächste Ereignisschleife beginnt, die normalen Phasen der Ereignisschleife werden ausgeführt, setImmediate
wird ausgelöst und ruft rekursiv unsere Schritt
-Funktion auf, um den Vorgang von vorne zu beginnen. Der aktuelle Betrieb endet, nextTick
wird ausgelöst usw.
Die Ausgabe des obigen Codes wäre:
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
Lassen Sie uns nun unseren rekursiven Aufruf von Schritt
in unseren nextTick
-Handler verlegen, anstatt im setImmediate
zu sein.
function Schritt(Iteration) {
if (Iteration === 10) return;
setImmediate(() => {
console.log(`setImmediate Iteration: ${Iteration}`);
});
process.nextTick(() => {
console.log(`nextTick Iteration: ${Iteration}`);
Schritt(Iteration + 1); // Rekursiver Aufruf vom nextTick-Handler.
});
}
Schritt(0);
Nachdem wir den rekursiven Aufruf von Schritt
in den nextTick
-Handler verschoben haben, wird sich das Verhalten in einer anderen Reihenfolge verhalten. Unsere erste Iteration der Ereignisschleife läuft und ruft Schritt
auf, wobei ein setImmediate
-Handler sowie ein nextTick
-Handler registriert werden. Nachdem der aktuelle Vorgang beendet ist, wird unser nextTick
-Handler ausgelöst, der rekursiv Schritt
aufruft und einen weiteren setImmediate
-Handler sowie einen weiteren nextTick
-Handler registriert. Da ein nextTick
-Handler nach dem aktuellen Betrieb ausgelöst wird, wird durch das Registrieren eines nextTick
-Handlers innerhalb eines nextTick
-Handlers der zweite Handler sofort nach Beendigung des aktuellen Handler-Betriebs ausgeführt. Die nextTick
-Handler werden weiterhin ausgelöst, wodurch die aktuelle Ereignisschleife daran gehindert wird, fortzufahren. Bevor ein einziger setImmediate
-Handler ausgelöst wird, werden alle unsere nextTick
-Handler durchgeführt.
Die Ausgabe des obigen Codes lautet wie folgt:
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
Es sei darauf hingewiesen, dass, wenn wir den rekursiven Aufruf nicht unterbrechen und ihn nach 10 Iterationen abbrechen, die nextTick
-Aufrufe weiterhin rekursiv ausgeführt werden und die Ereignisschleife niemals zur nächsten Phase übergehen lässt. So kann sich nextTick
, wenn es rekursiv verwendet wird, blockieren, während setImmediate
in der nächsten Ereignisschleife ausgelöst wird und das Einrichten eines weiteren setImmediate
-Handlers innerhalb eines solchen die aktuelle Ereignisschleife überhaupt nicht unterbricht, was es ermöglicht, dass sie die Phasen der Ereignisschleife normal ausführt.
PS - Ich stimme anderen Kommentatoren zu, dass die Namen der beiden Funktionen leicht ausgetauscht werden könnten, da nextTick
klingt, als würde es in der nächsten Ereignisschleife ausgelöst werden, anstatt am Ende der aktuellen, und das Ende der aktuellen Schleife ist "unmittelbarer" als der Beginn der nächsten Schleife. Na ja, das ist das, was wir bekommen, wenn eine API reift und die Menschen sich auf bestehende Schnittstellen verlassen.