Jak działa setTimeout w Node.JS?


112

Wydaje mi się, że po wykonaniu znajduje się w kolejce, ale czy w kolejce jest jakaś pewność, że wywoła się dokładnie po X milisekundach? A może inne ciężkie zadania znajdujące się wyżej w kolejce opóźnią to?


14
Żaden system operacyjny nie działający w czasie rzeczywistym nigdy nie zapewni gwarancji poważnej dokładności. Różne rzeczy w systemie mogą (i będą) przeszkadzać mechanizmowi timera.
Pointy

Odpowiedzi:


174

Semantyka setTimeout jest mniej więcej taka sama jak w przeglądarce internetowej: argument timeout to minimalna liczba ms oczekiwania przed wykonaniem, a nie gwarancja. Ponadto przekazanie 0, liczby innej niż liczba lub liczby ujemnej spowoduje, że zaczeka minimalną liczbę ms. W Node jest to 1 ms, ale w przeglądarkach może to być nawet 50 ms.

Powodem tego jest brak wywłaszczania JavaScript przez JavaScript. Rozważmy ten przykład:

setTimeout(function () {
  console.log('boo')
}, 100)
var end = Date.now() + 5000
while (Date.now() < end) ;
console.log('imma let you finish but blocking the event loop is the best bug of all TIME')

Przepływ tutaj jest:

  1. zaplanuj limit czasu na 100 ms.
  2. zajęty czekaj przez 5000 ms.
  3. powrót do pętli zdarzeń. sprawdź oczekujące zegary i wykonaj.

Gdyby tak nie było, można by jeden kawałek JavaScript „przerywać” inny. Musielibyśmy ustawić muteksy, semafory i tym podobne, aby kod taki jak ten nie był niezwykle trudny do rozważenia:

var a = 100;
setTimeout(function () {
  a = 0;
}, 0);
var b = a; // 100 or 0?

Jednowątkowość wykonywania JavaScript w Node sprawia, że ​​praca z nim jest znacznie prostsza niż w przypadku większości innych stylów współbieżności. Oczywiście kompromis polega na tym, że źle działająca część programu może zablokować całość nieskończoną pętlą.

Czy to lepszy demon do walki niż złożoność wyprzedzania? To zależy.


20
Otrzymujesz +1 za wyjątkowo dokładną console.logwiadomość.
Załóż pozew Moniki

Podoba mi się, jak dodałeś CZAS w swoim stringu. Bardzo dobre wyjaśnienie !!!
Alisson

43

Ideą nieblokowania jest to, że iteracje pętli są szybkie. Tak więc iteracja dla każdego taktu powinna zająć wystarczająco dużo czasu, aby setTimeout był dokładny z rozsądną precyzją (może być wyłączony o mniej niż 100 ms).

Teoretycznie jednak masz rację. Jeśli napiszę aplikację i zablokuję tick, to setTimeouts będzie opóźnione. Odpowiadając na pytanie, kto może zapewnić wykonanie setTimeouts na czas? Pisząc kod nieblokujący, możesz kontrolować stopień dokładności do prawie każdego rozsądnego poziomu dokładności.

Tak długo, jak javascript jest „jednowątkowy” pod względem wykonywania kodu (z wyłączeniem programów webowych i tym podobnych), zawsze tak się dzieje. Jednowątkowy charakter w większości przypadków jest ogromnym uproszczeniem, ale wymaga nieblokującego idiomu, aby odnieść sukces.

Wypróbuj ten kod w przeglądarce lub w węźle, a zobaczysz, że nie ma gwarancji dokładności, wręcz przeciwnie, setTimeout będzie bardzo późny:

var start = Date.now();

// expecting something close to 500
setTimeout(function(){ console.log(Date.now() - start); }, 500);

// fiddle with the number of iterations depending on how quick your machine is
for(var i=0; i<5000000; ++i){}

O ile interpreter nie zoptymalizuje pętli (czego nie robi w chrome), otrzymasz coś w tysiącach. Usuń pętlę, a zobaczysz, że na nosie jest 500 ...


9

Jedynym sposobem zapewnienia wykonania kodu jest umieszczenie logiki setTimeout w innym procesie.

Użyj modułu procesu potomnego, aby stworzyć nowy program node.js, który wykonuje logikę i przekazuje dane do tego procesu za pośrednictwem pewnego rodzaju strumienia (może tcp).

W ten sposób, nawet jeśli jakiś długi kod blokujący jest uruchomiony w twoim głównym procesie, twój proces potomny już się rozpoczął i umieścił setTimeout w nowym procesie i nowym wątku, a zatem będzie działał, kiedy tego oczekujesz.

Dalsze komplikacje występują na poziomie sprzętowym, na którym działa więcej wątków niż procesów, a zatem przełączanie kontekstu spowoduje (bardzo niewielkie) opóźnienia w stosunku do oczekiwanego czasu. Powinno to być pomijalne, a jeśli ma to znaczenie, musisz poważnie rozważyć, co próbujesz zrobić, dlaczego potrzebujesz takiej dokładności i jakiego rodzaju alternatywny sprzęt czasu rzeczywistego jest dostępny do wykonania zadania.

Ogólnie rzecz biorąc, używanie procesów podrzędnych i uruchamianie aplikacji z wieloma węzłami jako oddzielnych procesów wraz z modułem równoważenia obciążenia lub współdzielonym magazynem danych (np. Redis) jest ważne dla skalowania kodu.


9

setTimeoutjest rodzajem wątku , przechowuje operację przez określony czas i wykonuje ją.

setTimeout(function,time_in_mills);

w tym przypadku pierwszy argument powinien być typem funkcji; jako przykład, jeśli chcesz wydrukować swoje imię po 3 sekundach, kod powinien wyglądać jak poniżej.

setTimeout(function(){console.log('your name')},3000);

Kluczową kwestią do zapamiętania jest to, co kiedykolwiek chcesz zrobić za pomocą setTimeoutmetody, zrób to wewnątrz funkcji . Jeśli chcesz wywołać inną metodę, analizując niektóre parametry, Twój kod powinien wyglądać jak poniżej:

setTimeout(function(){yourOtherMethod(parameter);},3000);

8

setTimeout(callback,t)służy do wywołania zwrotnego po co najmniej t milisekundach . Rzeczywiste opóźnienie zależy od wielu czynników zewnętrznych, takich jak szczegółowość timera systemu operacyjnego i obciążenie systemu.

Istnieje więc możliwość, że zostanie wywołany nieco po ustalonym czasie, ale nigdy wcześniej nie zostanie wywołany.

Czasomierz nie może trwać dłużej niż 24,8 dni.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.