Dlaczego setTimeout nie anuluje mojej pętli?
Zastanawiałem się, ile razy Instrukcja JavaScript while
(w konsoli Chrome) może zwiększyć zmienną w milisekundzie, więc szybko napisałem ten fragment bezpośrednio do konsoli:
var run = true, i = 0;
setTimeout(function(){ run = false; }, 1);
while(run){ i++; }
Problem w tym, że działa w nieskończoność.Dlaczego tak się dzieje i jak Mogę to rozwiązać?
6 answers
To wraca do jednowątkowej natury JavaScript1. To co się dzieje to mniej więcej tak:
- Twoje zmienne są przypisane.
- planujesz funkcję, aby ustawić
run = false
. To jest zaplanowane do uruchomienia po bieżąca funkcja jest uruchomiona (lub cokolwiek innego jest aktualnie aktywne). - masz swoją nieskończoną pętlę i pozostań wewnątrz bieżącej funkcji.
- po twojej niekończącej się pętli ( never ), wywołanie zwrotne
setTimeout()
zostanie wykonane irun=false
.
Jak widzisz, podejście setTimeout()
nie zadziała tutaj. Możesz to obejść, sprawdzając czas w stanie while
, ale to zakłóci rzeczywisty pomiar.
1 przynajmniej dla bardziej praktycznych celów można go zobaczyć jako jednowątkowy. W rzeczywistości istnieje tzw. "pętla zdarzeń". W tej pętli wszystkie funkcje są kolejkowane do czasu ich wykonania. Jeśli ustawisz w kolejce nową funkcję, zostanie ona umieszczona w odpowiedniej pozycji wewnątrz tej kolejki. Po zakończeniu bieżącej funkcji silnik pobiera następną funkcję z kolejki (w odniesieniu do timingów wprowadzonych np. przez setTimeout()
i wykonuje ją.
W rezultacie w każdym momencie wykonywana jest tylko jedna funkcja, co sprawia, że wykonanie jest prawie jednowątkowe. Istnieją pewne wyjątki dla wydarzeń, które zostały omówione w poniższym linku.
Dla Referencja:
-
Https://stackoverflow.com/a/16757582/1169798
Tutaj podobny scenariusz został wyjaśniony bardziej szczegółowo
-
Https://stackoverflow.com/a/2734311/1169798
Bardziej szczegółowy opis kiedy JS może być postrzegany jako jednowątkowy, a kiedy nie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:53
JavaScript jest jednowątkowy, więc gdy jesteś w pętli, nic innego nie jest wykonywane.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-12 17:50:14
Aby zachować prawdziwą prędkość Chrome bez konieczności ciągłego pobierania czasu do obliczenia prędkości, możesz wypróbować ten kod JS:
var start = new Date().getTime()
var i = 0
while(i<1000000)
{
i++
}
var end = new Date().getTime()
var delta = end-start
var speed = i/delta
console.log(speed + " loops per millisecond")
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-09 13:29:39
Javascript jest jednowątkowy, co oznacza, że uruchamia tylko jedną instrukcję na raz, sekwencyjnie.
System zdarzeń, podobnie jak w wielu innych językach i bibliotekach, jest obsługiwany przez pętlę zdarzeń. Pętla zdarzeń jest w zasadzie pętlą, która na każdej iteracji sprawdza, czy nie ma wiadomości w kolejce, a zdarzenia dispatch.
W javascript (jak w mot języków implementujących ten wzorzec), pętla zdarzeń jest wywoływana, gdy stos jest pusty, to znaczy, gdy wszystkie funkcje mają zwroty, w innych word, na końcu kodu programu.
Twój "prawdziwy" program wygląda mniej więcej tak za sceną:
var run = true, i = 0;
setTimeout(function(){ run = false; }, 1);
while(run){ i++; }
while(true) {
/*
* check for new messages in the queue and dispatch
* events if there are some
*/
processEvents();
}
Więc wiadomość z zegara mówiąca, że timeout się skończył, nigdy nie jest przetwarzana.
Więcej informacji o pętli zdarzeń na : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/EventLoop
Oczywiście jest to nieco bardziej złożone, sprawdź tutaj te przykłady : czy JavaScript jest gwarantowany jako jednowątkowy? (tl; dr: W niektóre silniki przeglądarki, niektóre zdarzenia zewnętrzne nie są zależne od pętli zdarzeń i są natychmiast uruchamiane, gdy wystąpią, uprzedzając bieżące zadanie. Ale tak nie jest w przypadku setTimeout, który po prostu dodaje wiadomość do kolejki i nigdy nie jest uruchamiany od razu.)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:35
Pętla While nie ma dostępu do setTimeout. Masz kod, który ustawia run true, a wtedy nigdy nie stanie się fałszywy.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-09 20:06:28
JavaScript ma pojedynczy wątek i ma pojedynczy wątek w dowolnym miejscu.
Myślę, że to pytanie jest dobre: czy JavaScript może być jednowątkowy?
Gdy twój kod jest w pętli, inne ocde nie wykonują i nie blokują.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:29:37