Pamiętaj, że chociaż JavaScript jest jednowątkowy, wszystkie operacje we / wy węzła i wywołania natywnych interfejsów API są albo asynchroniczne (przy użyciu mechanizmów specyficznych dla platformy), albo działają w oddzielnym wątku. (To wszystko jest obsługiwane przez libuv.)
Tak więc, gdy w gnieździe są dostępne dane lub zwrócono natywną funkcję API, potrzebujemy zsynchronizowanego sposobu wywołania funkcji JavaScript zainteresowanej konkretnym zdarzeniem, które właśnie się wydarzyło.
Nie jest bezpieczne po prostu wywołanie funkcji JS z wątku, w którym zdarzenie natywne miało miejsce z tych samych powodów, które można spotkać w zwykłej aplikacji wielowątkowej - warunki wyścigu, nieatomowy dostęp do pamięci i tak dalej.
Więc to, co robimy, to umieszczanie zdarzenia w kolejce w sposób bezpieczny dla wątków. W nadmiernie uproszczonym psuedokodzie coś takiego:
lock (queue) {
queue.push(event);
}
Następnie wracając do głównego wątku JavaScript (ale po stronie C), robimy coś takiego:
while (true) {
lock (queue) {
var tickEvents = copy(queue);
queue.empty();
}
for (var i = 0; i < tickEvents.length; i++) {
InvokeJSFunction(tickEvents[i]);
}
}
Element while (true)
(który w rzeczywistości nie istnieje w kodzie źródłowym węzła; jest to czysto ilustracyjne) reprezentuje pętlę zdarzeń . Wewnętrzna for
wywołuje funkcję JS dla każdego zdarzenia, które było w kolejce.
To jest tik: synchroniczne wywołanie zera lub większej liczby funkcji zwrotnych skojarzonych ze zdarzeniami zewnętrznymi. Gdy kolejka zostanie opróżniona i ostatnia funkcja powróci, tik się skończył. Wracamy do początku (następny tik) i sprawdzamy, czy zdarzenia, które zostały dodane do kolejki z innych wątków podczas działania naszego JavaScript .
Co może dodawać rzeczy do kolejki?
process.nextTick
setTimeout
/setInterval
- I / O (od rzeczy
fs
, net
i tak dalej)
crypto
funkcje intensywnie wykorzystujące procesor, takie jak strumienie kryptograficzne, pbkdf2 i PRNG (które są w rzeczywistości przykładem ...)
- wszelkie natywne moduły, które używają kolejki roboczej libuv, aby synchroniczne wywołania bibliotek C / C ++ wyglądały asynchronicznie