2015-07-08 18 views
7

Pracowałem ze skryptami wykorzystującymi funkcję setInterval (fn, delay) w mojej aplikacji i po przeczytaniu o tym, jak działa setTimeout i JS, napotykam jakieś dziwne wyniki, więc zrobiłem test: Tutaj jest jsfiddle https://jsfiddle.net/jyq46uu1/2/JavaScript setTimeout nieoczekiwane wyjście

I kod jak sugeruje:

var limit  = 1000000000; 
var intervals = 0; 
var totalTime = new Date(); 
var startTime = new Date(); 

var uid = setInterval(
    function() { 
     // final lap? 
     if (intervals == 9) clearInterval(uid); 

     for (i = 0; i < limit; i += 1) { 
      // just working the CPU 
     } 
     // reduce the iterations 
     limit = limit/10; 
     intervals += 1;   

     console.log('Interval ' + intervals +' Time elapsed : ' + (new Date() - startTime)); 
     // reset the time 
     startTime = new Date(); 
    }, 250, 9); 

Ok, z tego co mam czerwone od http://ejohn.org/blog/how-javascript-timers-work/ JavaScript dokonać połączenia zegara dla funkcji w setInterval nawet jeśli „wątek jest zablokowany”, więc jeśli funkcja nadal wykonuje połączenie, po prostu czeka w kolejce i tak dalej ... w moim notebooku, który produkuje ten kod:

"Interval 1 Time elapsed : 4264" 
"Interval 2 Time elapsed : 477" 
"Interval 3 Time elapsed : 91" 
"Interval 4 Time elapsed : 170" 
"Interval 5 Time elapsed : 246" 
"Interval 6 Time elapsed : 242" 
"Interval 7 Time elapsed : 248" 
"Interval 8 Time elapsed : 248" 
"Interval 9 Time elapsed : 248" 

Ok, jeśli to, co mam czerwony jest prawdą, zanim skończył pierwszy przedział, wszystkie wywołania funkcji były w kolejce ... w moim skrypcie Redukuję pracę dla każdego wykonania, więc każde wezwanie powinno trwać mniej sekund niż poprzednia, LECZ bez względu na to, ile ustawiam iteracji, czas, który upłynął, zawsze podnosi tempo Interwału po 4. biegu. Może źle mi poszło, ale jeśli do czasu 4264 wszystkie funkcje są już w kolejce i mają działać natychmiast, powinny pokazywać mniej czasu, prawda? ... jeśli trzecia iteracja wyświetla 91, a pozostałe są tuż za nimi, powinny one wynosić 91 lub mniej. Ale tak nie jest.

Jeśli rozumiesz, co się dzieje, wyjaśnij mi to, ponieważ myślę, że czegoś brakuje.

Odpowiedz

1

Myślę, że pierwszy raz nie liczy się od początkowego interwału w kolejce, ale od momentu, w którym ustawiono startTime.

Następnie pierwszy licznik liczy czas inicjalizacji, tworzenie środowiska, przydzielanie zmiennych i timerów.

Spróbuj modyfikacji:

var limit  = 1000000000; 
var intervals = 0; 
var totalTime = new Date(); 
var startTime = false; 

var uid = setInterval(
    function() { 
     if (!startTime) startTime = new Date(); 
     // final lap? 
     if (intervals == 9) clearInterval(uid); 

     for (i = 0; i < limit; i += 1) { 
      // just working the CPU 
     } 
     // reduce the iterations 
     limit = limit/10; 
     intervals += 1;   

     console.log('Interval ' + intervals +' Time elapsed : ' + (new Date() - startTime)); 
     // reset the time 
     startTime = new Date(); 
    }, 250, 9); 

Ponadto, po raz pierwszy zaczyna już prawdopodobnie dlatego, że funkcja jest interpretowany, a następnie kolejne czasy jest wykonywany „skompilowany”/optymalizowana przez Javascript JIT (patrz https://en.wikipedia.org/wiki/Just-in-time_compilation) . Sprawdź ten kod, aby zobaczyć dowód

var limit  = 1000000000; 
var intervals = 0; 
var totalTime = new Date(); 
var startTime = new Date(); 

var uid = setInterval(
    function() { 
     startTime = new Date(); 
     // final lap? 
     if (intervals == 9) clearInterval(uid); 

     for (i = 0; i < limit; i += 1) { 
      // just working the CPU 
     } 
     // reduce the iterations 
     limit = limit/10; 
     intervals += 1;   

     console.log('Interval ' + intervals +' Time elapsed : ' + (new Date() - startTime)); 
     // reset the time 
     startTime = new Date(); 
    }, 250, 9); 

wyjście:

Interval 1 Time elapsed : 3249 
Interval 2 Time elapsed : 299 
Interval 3 Time elapsed : 31 
Interval 4 Time elapsed : 5 
Interval 5 Time elapsed : 0 
Interval 6 Time elapsed : 0 
Interval 7 Time elapsed : 0 
Interval 8 Time elapsed : 0 
Interval 9 Time elapsed : 0 
Interval 10 Time elapsed : 0 

setInterval nie jest dobry pomiar czasu, w którym powinien on kolejce następne wykonanie. Myślę, że ma to związek ze snem(). Nie jest niezawodny, jeśli proces pobiera procesor i zawiesza go (jak dla ciebie).

SetInterval gwarantuje tylko to:

  • liczba Wykonanie 1 nie wydarzy się w czasie krótszym niż 250 ms.
  • Egzekucja numer 2 nie nastąpi w czasie krótszym niż 2 * 250 ms.
  • Wykonanie nr 3 nie nastąpi w czasie krótszym niż 3 * 250 ms.
  • ...

setInterval nie gwarantuje, że niektóre wykonanie wydarzy się przed określonym czasie w przyszłości. To zależy od bieżącego użycia procesora.

Kolejkowanie i uruchamianie oczekujących funkcji zależy również od bieżącego użycia procesora.

Extra Common Porady Jeśli trzeba wykonać funkcję okresowo, polecam tej metody, która gwarantuje opóźnienie zawsze greather niż 250ms między egzekucji

var t = false; 
var fun = function() { 
    console.log("run"); 
    t = setTimeout(fun, 250); 
} 
fun(); 
+0

Intryguje mnie drugi kod ... Nie widzę różnicy od pierwszego, ale wynik jest wyraźnie inny –

+0

@GabrielMatusevich 'startTime = new Date();' u góry funkcji – Andreas

+0

Dodałem 'startTime = new Date(); 'jako pierwsza linia w funkcji, aby zobaczyć czas, który upłynął TYLKO dla funkcji, bez efektów ubocznych w pomiarze czasu z setinterval – jperelli

0

Nie jestem pewien, czy mam rację w tym i to jest to, co wiem o "Javascript".

Javascript jest oparty na wyzwalaniu opartym na zdarzeniach i występuje "brak kolejki i oczekiwanie". To będzie po prostu iść równolegle (coś jak wątki).

Sądzę, że to będzie sensowne, jak szybko to zinterpretuje i przejmie następne.

+3

JavaScript jest jednowątkowy ... –

+0

w ogóle @Alexander, proszę przeczytać tutaj. http://stackoverflow.com/questions/2734025/is-javascript-guaranteed-to-be-single-threaded –

+1

Przeczytaj najwyżej ocenione komentarze na temat tej odpowiedzi. Takie zachowanie nie powoduje, że JavaScript jest wielowątkowy. To zachowanie hosta, gdy skrypt wywołuje interfejsy API środowiska, które mogą wywoływać więcej kodu przed powrotem do kodu, który wywołał API. Żadne dwie sekcje twojego kodu nigdy nie będą wykonywane w tym samym czasie (bez pracownika, który jest). –

0

Cóż mam kilka punktów o tym mówić, patrz wyniki najpierw.

Results Image

„Nowy” Kod:

var limit  = 1000000000; 
var intervals = 0; 
var totalTime = new Date(); 

top.uid = setInterval(
    function() { 
     startTime = new Date(); 
     // final lap? 
     if (intervals == 9) clearInterval(top.uid); 

     for (i = 0; i < limit; i += 1) { 
      // just working the CPU 
     } 
     // reduce the iterations 
     limit = limit/10; 
     intervals += 1; 

     console.log('Interval ' + intervals +' Time elapsed : ' + (new Date() - startTime)); 
     // reset the time 
    }, 1); 

pamiętać, że usunięcie zmiennej startTime z linii 4 i przekazać je do początku funkcji, tak, czas obliczona jest taka sama do wszystkich połączeń. Zmieniłem TIMERA z 250ms na 1ms, więc nie trzeba obliczyć -250ms z każdego wyniku.

Nowe wyniki są dość spójne i pokazują, że czas przeznaczony na obliczenia jest bardzo łatwy i spójny.

Mam nadzieję, że pomogłem to wyjaśnić.

Powiązane problemy