Denken Sie daran, dass während JavaScript Single-Threaded ist, alle E / A und Aufrufe des Knotens an native APIs entweder asynchron sind (mithilfe plattformspezifischer Mechanismen) oder auf einem separaten Thread ausgeführt werden. (Dies wird alles über libuv erledigt.)
Wenn also Daten auf einem Socket verfügbar sind oder eine native API-Funktion zurückgegeben wurde, benötigen wir eine synchronisierte Methode, um die JavaScript-Funktion aufzurufen, die an dem gerade aufgetretenen Ereignis interessiert ist.
Es ist nicht sicher, die JS-Funktion einfach von dem Thread aus aufzurufen, in dem das native Ereignis aufgetreten ist, und zwar aus denselben Gründen, die in einer regulären Multithread-Anwendung auftreten würden - Race-Bedingungen, nicht-atomarer Speicherzugriff usw.
Wir stellen das Ereignis also threadsicher in eine Warteschlange. Im vereinfachten Pseudocode so etwas wie:
lock (queue) {
queue.push(event);
}
Dann, zurück zum Haupt-JavaScript- Thread (aber auf der C-Seite der Dinge), machen wir so etwas wie:
while (true) {
lock (queue) {
var tickEvents = copy(queue);
queue.empty();
}
for (var i = 0; i < tickEvents.length; i++) {
InvokeJSFunction(tickEvents[i]);
}
}
Das while (true)
(das im Quellcode des Knotens nicht vorhanden ist; dies ist nur zur Veranschaulichung) repräsentiert die Ereignisschleife . Das Innere for
ruft die JS-Funktion für jedes Ereignis auf, das sich in der Warteschlange befand.
Dies ist ein Häkchen: der synchrone Aufruf von null oder mehr Rückruffunktionen, die externen Ereignissen zugeordnet sind. Sobald die Warteschlange geleert ist und die letzte Funktion zurückkehrt, ist das Häkchen beendet. Wir kehren zum Anfang (dem nächsten Häkchen) zurück und suchen nach Ereignissen, die von anderen Threads zur Warteschlange hinzugefügt wurden, während unser JavaScript ausgeführt wurde .
Was kann der Warteschlange etwas hinzufügen?
process.nextTick
setTimeout
/.setInterval
- I / O (Stoff , aus
fs
, net
und so weiter)
crypto
Die prozessorintensiven Funktionen von Crypto Streams, pbkdf2 und PRNG (die eigentlich ein Beispiel für ... sind)
- Alle nativen Module, die die libuv-Arbeitswarteschlange verwenden , um synchrone C / C ++ - Bibliotheksaufrufe asynchron aussehen zu lassen