Ich denke, ich kann das ganz gut veranschaulichen. Da nextTick
es am Ende der aktuellen Operation aufgerufen wird, kann ein rekursiver Aufruf dazu führen, dass die Ereignisschleife nicht mehr fortgesetzt wird. setImmediate
Löst dies, indem in der Prüfphase der Ereignisschleife ausgelöst wird, sodass die Ereignisschleife normal fortgesetzt werden kann.
┌───────────────────────┐
┌─>│ timers │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ I/O callbacks │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ idle, prepare │
│ └──────────┬────────────┘ ┌───────────────┐
│ ┌──────────┴────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └──────────┬────────────┘ │ data, etc. │
│ ┌──────────┴────────────┐ └───────────────┘
│ │ check │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
└──┤ close callbacks │
└───────────────────────┘
Quelle: https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/
Beachten Sie, dass die Überprüfungsphase unmittelbar nach der Abfragephase liegt. Dies liegt daran, dass die Abfragephase und E / A-Rückrufe die wahrscheinlichsten Orte setImmediate
sind, an denen Ihre Anrufe ausgeführt werden. Im Idealfall sind die meisten dieser Aufrufe tatsächlich ziemlich unmittelbar, nur nicht so unmittelbar, wie nextTick
dies nach jeder Operation überprüft wird und technisch außerhalb der Ereignisschleife vorhanden ist.
Schauen wir uns ein kleines Beispiel für den Unterschied zwischen setImmediate
und an 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);
Angenommen, wir haben gerade dieses Programm ausgeführt und durchlaufen die erste Iteration der Ereignisschleife. Es wird die step
Funktion mit der Iteration Null aufgerufen . Anschließend werden zwei Handler registriert, einer für setImmediate
und einer für process.nextTick
. Wir rufen diese Funktion dann rekursiv vom setImmediate
Handler auf, der in der nächsten Prüfphase ausgeführt wird. Der nextTick
Handler wird am Ende der aktuellen Operation ausgeführt und unterbricht die Ereignisschleife. Obwohl er als zweiter registriert wurde, wird er tatsächlich zuerst ausgeführt.
Die Reihenfolge lautet: nextTick
Wird ausgelöst, wenn die aktuelle Operation endet, die nächste Ereignisschleife beginnt, normale Ereignisschleifenphasen ausgeführt werden, wird setImmediate
ausgelöst und ruft rekursiv unsere step
Funktion auf, um den Prozess erneut zu starten. Der aktuelle Betrieb endet, nextTick
Brände 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
Verschieben wir nun unseren rekursiven Aufruf step
in unseren nextTick
Handler anstelle des 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);
Nachdem wir den rekursiven Aufruf step
in den nextTick
Handler verschoben haben, verhalten sich die Dinge in einer anderen Reihenfolge. Unsere erste Iteration der Ereignisschleife wird ausgeführt und ruft die step
Registrierung eines setImmedaite
Handlers sowie eines nextTick
Handlers auf. Nach nextTick
Beendigung der aktuellen Operation wird unser Handler ausgelöst, der rekursiv einen step
anderen setImmediate
Handler sowie einen anderen nextTick
Handler aufruft und registriert . Da ein nextTick
Handler nach der aktuellen Operation ausgelöst wird , führt das Registrieren eines nextTick
Handlers in einem nextTick
Handler dazu, dass der zweite Handler unmittelbar nach Abschluss der aktuellen Handleroperation ausgeführt wird. Die nextTick
Handler feuern weiter und verhindern so, dass die aktuelle Ereignisschleife jemals fortgesetzt wird. Wir werden alle unsere durchstehennextTick
Handler, bevor wir ein einzelnes setImmediate
Handlerfeuer sehen.
Die Ausgabe des obigen Codes lautet:
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
Beachten Sie, dass, wenn wir den rekursiven Aufruf nicht unterbrochen und nach 10 Iterationen abgebrochen nextTick
hätten, die Aufrufe immer wieder rekursiv würden und die Ereignisschleife niemals zur nächsten Phase übergehen würde. Auf diese Weise nextTick
kann es bei rekursiver Verwendung blockiert werden, während setImmediate
das Auslösen in der nächsten Ereignisschleife und das Festlegen eines anderen setImmediate
Handlers innerhalb einer Schleife die aktuelle Ereignisschleife überhaupt nicht unterbricht, sodass die Phasen der Ereignisschleife wie gewohnt weiter ausgeführt werden können.
Ich hoffe, das hilft!
PS - Ich stimme anderen Kommentatoren zu, dass die Namen der beiden Funktionen leicht ausgetauscht werden könnten, da es so nextTick
klingt, als würde es in der nächsten Ereignisschleife und nicht am Ende der aktuellen ausgelöst, und das Ende der aktuellen Schleife ist "unmittelbarer" "als der Beginn der nächsten Schleife. Na ja, das bekommen wir, wenn eine API ausgereift ist und die Leute von vorhandenen Schnittstellen abhängig werden.