Czym dokładnie jest takt pętli zdarzeń Node.js?

84

Zagłębiłem się bardziej w wewnętrzne elementy architektury Node.js, a termin, który często pojawia się, to „tick”, jak w przypadku „next tick of the event loop” lub funkcja nextTick () .

To, czego nie widziałem, to solidna definicja tego, czym dokładnie jest „tik”. Opierając się na różnych artykułach ( takich jak ten ), udało mi się ułożyć koncepcję w mojej głowie, ale nie jestem pewien, czy jest dokładna.

Czy mogę uzyskać dokładny i szczegółowy opis taktu pętli zdarzeń Node.js?

d512
źródło
ponieważ jego „pętla” oznacza „przy następnym zapętleniu”, więc zaznaczenie to cała pętla, kończy się, gdy żadne zdarzenia nie są wyzwalane, a nodejs zapętlił wszystko, aby sprawdzić, czy któreś z nich zostało uruchomione, „nextTick” oznacza następne pętla po bieżącej.
Gntem

Odpowiedzi:

156

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) {
    // this is the beginning of a tick

    lock (queue) {
        var tickEvents = copy(queue); // copy the current queue items into thread-local memory
        queue.empty(); // ..and empty out the shared queue
    }

    for (var i = 0; i < tickEvents.length; i++) {
        InvokeJSFunction(tickEvents[i]);
    }

    // this the end of the tick
}

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 forwywoł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, neti tak dalej)
  • cryptofunkcje 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
josh3736
źródło
2
Tak, udało ci się to. Szczególnie zastanawiałem się nad kopiowaniem kolejki i przeglądaniem wszystkich zdarzeń na kopii. Ale teraz ma to dużo sensu. Dzięki.
d512
Czy jest to słynny „wzorzec iteracji asynchronicznej”?
Stef
1
@sanjeev, co rozumiesz przez „stałą pracę”? Jedyne, co robi aplikacja JavaScript, to przetwarzanie zdarzeń.
josh3736
2
Chciałbym dodać, że w 0.10.x setImmediaterównież kolejkuje funkcję.
DanielKhan
1
Czy tick oznacza fazę pętli zdarzenia?
faressoft
10

Prostsza odpowiedź dla nowicjuszy w JavaScript:

Pierwszą rzeczą do zrozumienia jest to, że JavaScript jest „środowiskiem jednowątkowym”. Odnosi się to do zachowania JavaScript polegającego na wykonywaniu bloków kodu pojedynczo z „pętli zdarzeń” w pojedynczym wątku. Poniżej znajduje się podstawowa implementacja pętli zdarzeń zaczerpnięta z książki Kyle'a Simpsona ydkJS, a następnie wyjaśnienie:

// `eventLoop` is an array that acts as a queue (first-in, first-out)
var eventLoop = [ ];
var event;

// keep going "forever"
while (true) {
    // perform a "tick"
    if (eventLoop.length > 0) {
        // get the next event in the queue
        event = eventLoop.shift();

        // now, execute the next event
        try {
            event();
        }
        catch (err) {
            reportError(err);
        }
    }
}

Pierwsza pętla while symuluje pętlę zdarzeń. Tik to dekolejkowanie zdarzenia z „kolejki pętli zdarzeń” i wykonanie tego zdarzenia.

Zobacz odpowiedź „Josh3796”, aby uzyskać bardziej szczegółowe wyjaśnienie tego, co dzieje się podczas dekolejowania i wykonywania zdarzenia.

Polecam również przeczytanie książki Kyle'a Simpsona tym, którzy chcą dogłębnie zrozumieć JavaScript. Jest całkowicie darmowy i typu open source i można go znaleźć pod tym linkiem: https://github.com/getify/You-Dont-Know-JS

Konkretną sekcję, do której się odwołałem, można znaleźć tutaj: https://github.com/getify/You-Dont-Know-JS/blob/2nd-ed/sync-async/ch1.md

Parm
źródło
1

Bardzo prosta i krótka metoda zaznaczania pętli zdarzeń to:

Jest używany przez wewnętrzny mechanizm węzła, w którym po przetworzeniu zestawu żądań w kolejce inicjowany jest tick, który reprezentuje zakończenie zadania

Aks
źródło
czy możesz podać źródło swojej odpowiedzi?
Kick Buttowski 08.04.2020