setImmediate vs. nextTick

336

Node.js Version 0.10 wurde heute veröffentlicht und eingeführt setImmediate. In der API-Änderungsdokumentation wird vorgeschlagen , sie bei rekursiven nextTickAufrufen zu verwenden.

Nach dem, was MDN sagt , scheint es sehr ähnlich zu sein process.nextTick.

Wann soll ich verwenden nextTickund wann soll ich verwenden setImmediate?

Benjamin Gruenbaum
quelle
20
Es gibt 5 Absätze über diese Änderung auf dem Blog blog.nodejs.org/2013/03/11/node-v0-10-0-stable
mak
1
Aus Leistungsbenchmarks nextTickgeht hervor, dass dies schneller ist als setImmediatebei großen Berechnungen.
10
Für die Aufzeichnung habe ich diese fünf Absätze zuerst gelesen und bin immer noch zu dieser Frage gekommen, als sie mir nichts wirklich klar machte. Die akzeptierte Antwort ist viel prägnanter und beschreibt tatsächlich setImmediatedetaillierter, was funktioniert.
Chev
Ich habe den Unterschied in meinem Blog ausführlich erklärt .
Plafer
Ist es der Fall, dass GC vorher ausgeführt werden kann setImmediate, aber nicht vorher nextTick?

Antworten:

510

Verwenden setImmediateSie diese Option, wenn Sie die Funktion hinter den E / A-Ereignisrückrufen in die Warteschlange stellen möchten, die sich bereits in der Ereigniswarteschlange befinden. Verwenden Sie process.nextTickdiese Option, um die Funktion am Anfang der Ereigniswarteschlange effektiv in die Warteschlange zu stellen, sodass sie unmittelbar nach Abschluss der aktuellen Funktion ausgeführt wird.

In einem Fall, in dem Sie versuchen, einen lang laufenden, CPU-gebundenen Job mithilfe der Rekursion aufzubrechen, möchten Sie jetzt die nächste Iteration verwenden, setImmediateanstatt sie process.nextTickin die Warteschlange zu stellen, da sonst Rückrufe von E / A-Ereignissen keine Chance hätten zwischen Iterationen laufen.

JohnnyHK
quelle
86
Rückrufe, die an process.nextTick übergeben werden, werden normalerweise am Ende des aktuellen Ausführungsflusses aufgerufen und sind daher ungefähr so ​​schnell wie das synchrone Aufrufen einer Funktion. Wenn diese Option nicht aktiviert ist, wird die Ereignisschleife ausgehungert und das Auftreten von E / A verhindert. setImmediates werden in der erstellten Reihenfolge in die Warteschlange gestellt und einmal pro Schleifeniteration aus der Warteschlange entfernt. Dies unterscheidet sich von process.nextTick, bei dem pro Iteration Rückrufe in der Warteschlange von process.maxTickDepth ausgeführt werden. setImmediate gibt der Ereignisschleife nach dem Auslösen eines Rückrufs in der Warteschlange nach, um sicherzustellen, dass die E / A nicht ausgehungert werden.
Benjamin Gruenbaum
2
@UstamanSangat setImmediate wird nur von IE10 + unterstützt. Alle anderen Browser weigern sich hartnäckig, den wahrscheinlichen zukünftigen Standard zu implementieren, da sie nicht gerne von Microsoft geschlagen werden. Um ein ähnliches Ergebnis in FF / Chrome zu erzielen, können Sie postMessage verwenden (eine Nachricht in Ihrem eigenen Fenster veröffentlichen). Sie können auch requestAnimationFrame verwenden, insbesondere wenn Ihre Updates mit der Benutzeroberfläche zusammenhängen. setTimeout (func, 0) funktioniert überhaupt nicht wie process.nextTick.
Fabspro
44
@fabspro "weil sie es nicht mögen, von meinem Microsoft geschlagen zu werden" lässt dich wegen etwas wund klingen. Es ist hauptsächlich, weil es schrecklich, schrecklich benannt ist. Wenn es eine einmalige setImmediate-Funktion gibt, die niemals ausgeführt wird, ist sie sofort verfügbar. Der Name der Funktion ist genau das Gegenteil von dem, was sie tut. nextTick und setImmediate sollten besser umschalten. setImmediate wird unmittelbar nach Abschluss des aktuellen Stapels (vor dem Warten auf E / A) und nextTick am Ende des nächsten Ticks (nach dem Warten auf E / A) ausgeführt. Aber das wurde schon tausendmal gesagt.
Craig Andrews
4
@fabspro Aber leider heißt die Funktion nextTick. nextTick wird "sofort" ausgeführt, während setImmediate eher einem setTimeout / postMessage ähnelt.
Robert
1
@CraigAndrews würde ich vermeiden, requestAnimationFrameda es nicht immer vorkommt (ich habe dies definitiv gesehen, ich denke, Beispiel war Tab war nicht aktueller Tab) und es kann aufgerufen werden, bevor die Seite fertig gemalt wurde (dh der Browser ist noch mit Zeichnen beschäftigt).
Robocat
68

Als Illustration

import fs from 'fs';
import http from 'http';

const options = {
  host: 'www.stackoverflow.com',
  port: 80,
  path: '/index.html'
};

describe('deferredExecution', () => {
  it('deferredExecution', (done) => {
    console.log('Start');
    setTimeout(() => console.log('TO1'), 0);
    setImmediate(() => console.log('IM1'));
    process.nextTick(() => console.log('NT1'));
    setImmediate(() => console.log('IM2'));
    process.nextTick(() => console.log('NT2'));
    http.get(options, () => console.log('IO1'));
    fs.readdir(process.cwd(), () => console.log('IO2'));
    setImmediate(() => console.log('IM3'));
    process.nextTick(() => console.log('NT3'));
    setImmediate(() => console.log('IM4'));
    fs.readdir(process.cwd(), () => console.log('IO3'));
    console.log('Done');
    setTimeout(done, 1500);
  });
});

gibt die folgende Ausgabe

Start
Done
NT1
NT2
NT3
TO1
IO2
IO3
IM1
IM2
IM3
IM4
IO1

Ich hoffe, dies kann helfen, den Unterschied zu verstehen.

Aktualisiert:

Rückrufe, die mit process.nextTick()run zurückgestellt wurden, bevor ein anderes E / A-Ereignis ausgelöst wurde, während mit setImmediate () die Ausführung hinter jedem E / A-Ereignis in die Warteschlange gestellt wird, das sich bereits in der Warteschlange befindet.

Node.js Design Patterns von Mario Casciaro (wahrscheinlich das beste Buch über node.js / js)

DraganS
quelle
2
Das ist wirklich hilfreich, danke. Ich denke, Bilder und Beispiele sind der schnellste Weg, etwas zu verstehen.
John James
1
Ich denke, es ist wichtig darauf hinzuweisen, dass setTimeout () und setImmediate (), wenn nicht innerhalb eines E / A-Zyklus, die Reihenfolge abhängig von der Leistung eines Prozesses nicht deterministisch ist. nodejs.org/en/docs/guides/event-loop-timers-and-nexttick For example, if we run the following script which is not within an I/O cycle (i.e. the main module), the order in which the two timers are executed is non-deterministic, as it is bound by the performance of the process: Diese Antwort beantwortet also nicht wirklich den genauen Unterschied, sondern lediglich ein Beispiel, das in verschiedenen Kontexten variieren kann
Actung
Wie von @Actung hervorgehoben. Es ist sehr wichtig zu wissen, ob sich setTimetout und setImmediate innerhalb eines E / A-Zyklus befinden oder nicht, um das Ergebnis zu bestimmen.
Rajika Imal
50

Ich denke, ich kann das ganz gut veranschaulichen. Da nextTickes am Ende der aktuellen Operation aufgerufen wird, kann ein rekursiver Aufruf dazu führen, dass die Ereignisschleife nicht mehr fortgesetzt wird. setImmediateLö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 setImmediatesind, an denen Ihre Anrufe ausgeführt werden. Im Idealfall sind die meisten dieser Aufrufe tatsächlich ziemlich unmittelbar, nur nicht so unmittelbar, wie nextTickdies 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 setImmediateund 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 stepFunktion mit der Iteration Null aufgerufen . Anschließend werden zwei Handler registriert, einer für setImmediateund einer für process.nextTick. Wir rufen diese Funktion dann rekursiv vom setImmediateHandler auf, der in der nächsten Prüfphase ausgeführt wird. Der nextTickHandler 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: nextTickWird ausgelöst, wenn die aktuelle Operation endet, die nächste Ereignisschleife beginnt, normale Ereignisschleifenphasen ausgeführt werden, wird setImmediateausgelöst und ruft rekursiv unsere stepFunktion auf, um den Prozess erneut zu starten. Der aktuelle Betrieb endet, nextTickBrä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 stepin unseren nextTickHandler 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 stepin den nextTickHandler verschoben haben, verhalten sich die Dinge in einer anderen Reihenfolge. Unsere erste Iteration der Ereignisschleife wird ausgeführt und ruft die stepRegistrierung eines setImmedaiteHandlers sowie eines nextTickHandlers auf. Nach nextTickBeendigung der aktuellen Operation wird unser Handler ausgelöst, der rekursiv einen stepanderen setImmediateHandler sowie einen anderen nextTickHandler aufruft und registriert . Da ein nextTickHandler nach der aktuellen Operation ausgelöst wird , führt das Registrieren eines nextTickHandlers in einem nextTickHandler dazu, dass der zweite Handler unmittelbar nach Abschluss der aktuellen Handleroperation ausgeführt wird. Die nextTickHandler feuern weiter und verhindern so, dass die aktuelle Ereignisschleife jemals fortgesetzt wird. Wir werden alle unsere durchstehennextTickHandler, bevor wir ein einzelnes setImmediateHandlerfeuer 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 nextTickhätten, die Aufrufe immer wieder rekursiv würden und die Ereignisschleife niemals zur nächsten Phase übergehen würde. Auf diese Weise nextTickkann es bei rekursiver Verwendung blockiert werden, während setImmediatedas Auslösen in der nächsten Ereignisschleife und das Festlegen eines anderen setImmediateHandlers 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 nextTickklingt, 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.

Chev
quelle
2
Ziemlich klare Beschreibung. Ich denke, diese Antwort braucht mehr positive Stimmen.
Actung
Gut erklärt (Y)
Dhiraj Sharma
Es ist wichtig, die Warnung von Node bezüglich der Verwendung von process.nextTick zu wiederholen. Wenn Sie eine große Anzahl von Rückrufen in der nextTickQueue in die Warteschlange stellen, können Sie die Ereignisschleife möglicherweise verhungern lassen, indem Sie sicherstellen, dass die Abfragephase nie erreicht wird. Aus diesem Grund sollten Sie setImmediate generell bevorzugen.
Faridcs
1
Danke, das war die beste Erklärung. Der Beispielcode hat wirklich geholfen.
Skyhavoc
@skyhavoc froh, dass ich helfen konnte!
Chev
30

In den Kommentaren in der Antwort wird nicht explizit angegeben, dass nextTick von Makrosemantik zu Mikrosemantik verschoben wurde.

Vor dem Knoten 0.9 (als setImmediate eingeführt wurde) wurde nextTick zu Beginn des nächsten Callstacks ausgeführt.

Seit Knoten 0.9 wird nextTick am Ende des vorhandenen Callstacks ausgeführt, während setImmediate am Anfang des nächsten Callstacks steht

Tools und Details finden Sie unter https://github.com/YuzuJS/setImmediate

Jay Day Zee
quelle
11

In einfachen Worten, process.NextTick () würde beim nächsten Tick der Ereignisschleife ausgeführt. Das setImmediate verfügt jedoch grundsätzlich über eine separate Phase, die sicherstellt, dass der unter setImmediate () registrierte Rückruf erst nach der E / A-Rückruf- und Abfragephase aufgerufen wird.

Weitere Informationen finden Sie unter diesem Link: https://medium.com/the-node-js-collection/what-you-should-know-to-really-understand-the-node-js-event-loop-and -its -metrics-c4907b19da4c

vereinfachte Ereignisschleifenereignisse

Ingwer Bier
quelle
7

Hier einige großartige Antworten, die detailliert beschreiben, wie beide funktionieren.

Fügen Sie einfach eine hinzu, die die gestellte Frage beantwortet:

Wann soll ich verwenden nextTickund wann soll ich verwenden setImmediate?


Immer benutzen setImmediate.


Die Node.js-Ereignisschleife, die Timer und dasprocess.nextTick() Dokument enthalten Folgendes:

Wir empfehlen Entwicklern die Verwendung setImmediate()in allen Fällen, da es einfacher ist, darüber nachzudenken (und dies zu Code führt, der mit einer größeren Vielfalt von Umgebungen wie Browser JS kompatibel ist).


Früher im Dokument warnt es, dass process.nextTick...

einige schlechte Situationen , weil es Ihnen erlaubt , zu „verhungern“ Ihre I / O durch rekursive machen process.nextTick()Anrufe , die vom Erreichen der die Ereignisschleife verhindert Umfrage Phase.

Wie sich herausstellt, process.nextTickkann sogar verhungern Promises:

Promise.resolve().then(() => { console.log('this happens LAST'); });

process.nextTick(() => {
  console.log('all of these...');
  process.nextTick(() => {
    console.log('...happen before...');
    process.nextTick(() => {
      console.log('...the Promise ever...');
      process.nextTick(() => {
        console.log('...has a chance to resolve');
      })
    })
  })
})

Auf der anderen Seite setImmediateist es " einfacher zu überlegen " und vermeidet diese Art von Problemen:

Promise.resolve().then(() => { console.log('this happens FIRST'); });

setImmediate(() => {
  console.log('this happens LAST');
})

So sei denn , es gibt einen besonderen Bedarf für das einzigartige Verhalten process.nextTick, ist der empfohlene Ansatz zur „ Verwendung setImmediate()in allen Fällen “.

Brian Adams
quelle
1

Ich empfehle Ihnen, den Abschnitt zu Dokumenten zu lesen , der für Loop vorgesehen ist, um ein besseres Verständnis zu erhalten. Ein Ausschnitt von dort:

Wir haben zwei Anrufe, die für Benutzer ähnlich sind, deren Namen jedoch verwirrend sind.

  • process.nextTick () wird sofort in derselben Phase ausgelöst

  • setImmediate () wird bei der folgenden Iteration oder 'Tick' der
    Ereignisschleife ausgelöst

Im Wesentlichen sollten die Namen ausgetauscht werden. process.nextTick () wird sofort ausgelöst als setImmediate (), aber dies ist ein Artefakt der Vergangenheit, das sich wahrscheinlich nicht ändern wird.

Valikhan Akhmedov
quelle