Niedawno napotkałem dość paskudny błąd, w którym kod ładował się <select>
dynamicznie przez JavaScript. Ta dynamicznie ładowana <select>
miała wstępnie wybraną wartość. W IE6, mieliśmy już kod naprawić wybrany <option>
, ponieważ czasami <select>
jest selectedIndex
wartość byłby zsynchronizowany z wybranym <option>
's index
atrybut, jak poniżej:
field.selectedIndex = element.index;
Jednak ten kod nie działał. Nawet jeśli pole selectedIndex
zostało ustawione poprawnie, niewłaściwy indeks ostatecznie zostałby wybrany. Jeśli jednak wstawię alert()
instrukcję w odpowiednim czasie, wybrana zostanie właściwa opcja. Myśląc, że to może być jakiś problem z czasem, wypróbowałem coś losowego, co widziałem wcześniej w kodzie:
var wrapFn = (function() {
var myField = field;
var myElement = element;
return function() {
myField.selectedIndex = myElement.index;
}
})();
setTimeout(wrapFn, 0);
I to zadziałało!
Mam rozwiązanie mojego problemu, ale nie jestem pewien, czy nie wiem dokładnie, dlaczego to rozwiązuje mój problem. Czy ktoś ma oficjalne wyjaśnienie? Jakiego problemu z przeglądarką unikam, nazywając moją funkcję „później” przy użyciu setTimeout()
?
źródło
setTimeout(fn)
jest taki sam jaksetTimeout(fn, 0)
, btw.Odpowiedzi:
W pytaniu istniał warunek wyścigu między:
Twój kod konsekwentnie wygrywał ten wyścig i próbował ustawić rozwijany wybór, zanim przeglądarka była gotowa, co oznacza, że pojawi się błąd.
Ta rasa istniała, ponieważ JavaScript ma jeden wątek wykonania, który jest współdzielony z renderowaniem strony. W efekcie uruchomienie JavaScript blokuje aktualizację DOM.
Twoje obejście polegało na:
Wywołanie
setTimeout
z wywołaniem zwrotnym, a zero jako drugi argument, zaplanuje, aby wywołanie zwrotne było uruchamiane asynchronicznie , po jak najkrótszym możliwym opóźnieniu - co będzie około 10 ms, gdy karta jest aktywna, a wątek wykonywania JavaScript nie jest zajęty.Rozwiązaniem OP było zatem opóźnienie o około 10 ms, ustawienie wybranego indeksu. To dało przeglądarce możliwość zainicjowania DOM, naprawiając błąd.
Każda wersja programu Internet Explorer wykazywała dziwaczne zachowania, a tego rodzaju obejście było czasami konieczne. Ewentualnie mógł to być prawdziwy błąd w bazie kodu PO.
Zobacz przemówienie Philipa Robertsa „Co do cholery jest pętlą zdarzeń?” po dokładniejsze wyjaśnienia.
źródło
Przedmowa:
Niektóre inne odpowiedzi są poprawne, ale w rzeczywistości nie ilustrują rozwiązania problemu, dlatego stworzyłem tę odpowiedź, aby przedstawić tę szczegółową ilustrację.
W związku z tym zamieszczam szczegółowy przegląd tego, co robi przeglądarka i jak
setTimeout()
pomaga korzystanie z niej . Wygląda na długotrwały, ale w rzeczywistości jest bardzo prosty i bezpośredni - właśnie uczyniłem go bardzo szczegółowym.AKTUALIZACJA: Zrobiłem JSFiddle, aby zademonstrować na żywo wyjaśnienie poniżej: http://jsfiddle.net/C2YBE/31/ . Wiele dzięki do @ThangChung za pomoc, aby go kickstart.
AKTUALIZACJA2: Na wypadek śmierci strony JSFiddle lub usunięcia kodu, dodałem kod do tej odpowiedzi na samym końcu.
SZCZEGÓŁY :
Wyobraź sobie aplikację internetową z przyciskiem „zrób coś” i div div.
Moduł
onClick
obsługi przycisku „zrób coś” wywołuje funkcję „LongCalc ()”, która wykonuje 2 czynności:Wykonuje bardzo długie obliczenia (powiedzmy, że zajmuje 3 minuty)
Drukuje wyniki obliczeń w div wyniku.
Teraz użytkownicy zaczynają testować to, klikają przycisk „zrób coś”, a strona siedzi tam, robiąc pozornie nic przez 3 minuty, stają się niespokojni, klikają przycisk ponownie, czekają 1 minutę, nic się nie dzieje, ponownie przyciskają ...
Problem jest oczywisty - chcesz DIV „Status”, który pokazuje, co się dzieje. Zobaczmy, jak to działa.
Więc dodajesz DIV „Status” (początkowo pusty) i modyfikujesz
onclick
moduł obsługi (funkcjęLongCalc()
), aby zrobić 4 rzeczy:Wypełnij status „Obliczanie ... może zająć ~ 3 minuty” do statusu DIV
Wykonuje bardzo długie obliczenia (powiedzmy, że zajmuje 3 minuty)
Drukuje wyniki obliczeń w div wyniku.
Wpisz status „Obliczenie zakończone” w status DIV
Z przyjemnością dajesz aplikację użytkownikom do ponownego przetestowania.
Wracają do ciebie wyglądając na bardzo wściekłych. I wyjaśnij, że kiedy kliknęli przycisk, status DIV nigdy nie był aktualizowany o status „Obliczanie ...” !!!
Drapiesz się po głowie, pytasz na StackOverflow (lub czytasz dokumenty lub google) i zdajesz sobie sprawę z problemu:
Przeglądarka umieszcza wszystkie zadania „DO ZROBIENIA” (zarówno zadania interfejsu użytkownika, jak i polecenia JavaScript) wynikające ze zdarzeń w jednej kolejce . I niestety ponowne narysowanie DIV „Status” z nową wartością „Obliczanie ...” to osobne TODO, które trafia na koniec kolejki!
Oto rozkład zdarzeń podczas testu użytkownika, zawartość kolejki po każdym zdarzeniu:
[Empty]
[Execute OnClick handler(lines 1-4)]
[Execute OnClick handler(lines 2-4), re-draw Status DIV with new "Calculating" value]
. Należy pamiętać, że podczas gdy zmiany DOM następują natychmiast, aby ponownie narysować odpowiedni element DOM, potrzebne jest nowe zdarzenie, wywołane zmianą DOM, które poszło na końcu kolejki .[Execute OnClick handler(lines 3-4), re-draw Status DIV with "Calculating" value]
.[Execute OnClick handler(line 4), re-draw Status DIV with "Calculating" value, re-draw result DIV with result]
.[Execute OnClick handler, re-draw Status DIV with "Calculating" value, re-draw result DIV with result; re-draw Status DIV with "DONE" value]
.return
zonclick
sub-programu obsługi. Usuwamy „Execute OnClick handler” z kolejki i rozpoczynamy wykonywanie następnego elementu w kolejce.Tak więc podstawowym problemem jest to, że zdarzenie ponownego losowania DIV „Status” jest umieszczane w kolejce na końcu, PO zdarzeniu „wykonaj linię 2”, które zajmuje 3 minuty, więc faktyczne ponowne losowanie nie następuje, dopóki PO zakończeniu obliczeń.
Na ratunek przychodzi
setTimeout()
. Jak to pomaga? Ponieważ poprzez wywoływanie długo działającego kodusetTimeout
, faktycznie tworzysz 2 zdarzenia:setTimeout
wykonanie i (z powodu przekroczenia limitu czasu 0) osobny wpis kolejki dla wykonywanego kodu.Tak więc, aby rozwiązać problem, modyfikujesz swój
onClick
moduł obsługi, aby był DWIEMI instrukcjami (w nowej funkcji lub tylko w blokuonClick
):Wypełnij status „Obliczanie ... może zająć ~ 3 minuty” do statusu DIV
Wykonaj
setTimeout()
z limitem czasu 0 i wywołaniemLongCalc()
funkcji .LongCalc()
funkcja jest prawie taka sama jak poprzednio, ale oczywiście nie ma aktualizacji DIV statusu „Obliczanie ...” jako pierwszego kroku; i zamiast tego natychmiast rozpoczyna obliczenia.Jak teraz wygląda sekwencja zdarzeń i kolejka?
[Empty]
[Execute OnClick handler(status update, setTimeout() call)]
[Execute OnClick handler(which is a setTimeout call), re-draw Status DIV with new "Calculating" value]
.[re-draw Status DIV with "Calculating" value]
. W kolejce nie ma nic nowego przez kolejne 0 sekund.[re-draw Status DIV with "Calculating" value, execute LongCalc (lines 1-3)]
.[execute LongCalc (lines 1-3)]
. Pamiętaj, że to ponowne losowanie może nastąpić PRZED włączeniem alarmu, który działa równie dobrze.Brawo! Status DIV właśnie został zaktualizowany do „Obliczania ...” przed rozpoczęciem obliczeń !!!
Poniżej znajduje się przykładowy kod z JSFiddle ilustrujący te przykłady: http://jsfiddle.net/C2YBE/31/ :
Kod HTML:
Kod JavaScript: (Wykonany
onDomReady
i może wymagać jQuery 1.9)źródło
Przeczytaj artykuł Johna Resiga na temat działania timerów JavaScript . Ustawienie limitu czasu powoduje, że kod asynchroniczny jest w kolejce, dopóki silnik nie wykona bieżącego stosu wywołań.
źródło
setTimeout()
kupuje ci trochę czasu do załadowania elementów DOM, nawet jeśli jest ustawiony na 0.Sprawdź to: setTimeout
źródło
Większość przeglądarek ma proces zwany głównym wątkiem, który jest odpowiedzialny za wykonywanie niektórych zadań JavaScript, aktualizacji interfejsu użytkownika, np .: malowanie, przerysowanie lub ponowne wlanie itp.
Niektóre zadania JavaScript i aktualizacji interfejsu użytkownika są umieszczane w kolejce w kolejce komunikatów przeglądarki, a następnie wysyłane do głównego wątku przeglądarki, który ma zostać wykonany.
Gdy aktualizacje interfejsu użytkownika są generowane, gdy główny wątek jest zajęty, zadania są dodawane do kolejki komunikatów.
setTimeout(fn, 0);
dodaj tofn
na końcu kolejki do wykonania. Planuje dodanie zadania do kolejki komunikatów po upływie określonego czasu.źródło
add this fn to the end of the queue
. Najważniejsze jest to, gdzie dokładniesetTimeout
dodaje tę funkcję, koniec tego cyklu pętli lub sam początek następnego cyklu pętli.Są tu sprzeczne uprzywilejowane odpowiedzi i bez dowodów nie ma sposobu, aby wiedzieć, w kogo uwierzyć. Oto dowód, że @DVK ma rację, a @SalvadorDali jest niepoprawny. Ten ostatni twierdzi:
Limit czasu wynoszący 4 ms nie ma znaczenia dla tego, co się dzieje. Tak naprawdę dzieje się tak, że setTimeout wypycha funkcję zwrotną na koniec kolejki wykonawczej. Jeśli po setTimeout (wywołanie zwrotne, 0) masz kod blokujący, którego uruchomienie zajmuje kilka sekund, wywołanie zwrotne nie zostanie wykonane przez kilka sekund, dopóki kod blokujący nie zakończy się. Wypróbuj ten kod:
Dane wyjściowe to:
źródło
Jednym z powodów tego jest odroczenie wykonania kodu w osobnej, kolejnej pętli zdarzeń. Reagując na jakieś zdarzenie w przeglądarce (na przykład kliknięcie myszą), czasami konieczne jest wykonanie operacji dopiero po przetworzeniu bieżącego zdarzenia.
setTimeout()
Zakład jest najprostszym sposobem, aby to zrobić.edytuj teraz, gdy jest 2015, powinienem zauważyć, że jest też coś
requestAnimationFrame()
, co nie jest dokładnie takie samo, ale jest wystarczająco bliskosetTimeout(fn, 0)
, że warto o nim wspomnieć.źródło
To stare pytania ze starymi odpowiedziami. Chciałem dodać nowe spojrzenie na ten problem i odpowiedzieć, dlaczego tak się dzieje, a nie dlaczego jest to przydatne.
Masz więc dwie funkcje:
a następnie zadzwoń do nich w następującej kolejności
f1(); f2();
aby zobaczyć, że drugi został wykonany jako pierwszy.I oto dlaczego: nie można mieć
setTimeout
opóźnienia 0 milisekund. Wartość minimalna jest określona przez przeglądarkę i nie jest 0 milisekund. Historycznie przeglądarki ustawiają to minimum na 10 milisekund, ale specyfikacje HTML5 i nowoczesne przeglądarki ustawiają to na 4 milisekundy.Również z mozilli:
Informacje PS są pobierane po przeczytaniu następującego artykułu .
źródło
setTimeout(fn,0)
jest użyteczne.setTimeout
/setInterval
od ponad pięciu poziomów głębokości; jeśli tego nie zrobisz, minimum wynosi 0 ms (co w przypadku braku wehikułów czasu). Dokumenty Mozilli rozszerzają tęsetInterval
funkcję , aby objąć powtarzające się, nie tylko zagnieżdżone przypadki (więc z interwałem 0 natychmiast ponownie zaplanuje kilka razy, a następnie opóźni później), ale proste zastosowaniasetTimeout
z minimalnym zagnieżdżaniem mogą natychmiast ustawiać się w kolejce.Ponieważ jest przekazywany przez czas trwania
0
, przypuszczam, że ma to na celu usunięcie kodu przekazanego dosetTimeout
przepływu wykonywania. Więc jeśli jest to funkcja, która może chwilę potrwać, nie uniemożliwi to wykonania następnego kodu.źródło
Obie te najwyżej ocenione odpowiedzi są błędne. Sprawdź opis MDN na modelu współbieżności i pętli zdarzeń , a powinno być jasne, co się dzieje (ten zasób MDN to prawdziwy klejnot). I po prostu za pomocą
setTimeout
może dodawać nieoczekiwane problemy w kodzie oprócz „rozwiązania” tego małego problemu.Co właściwie dzieje się tutaj, nie polega na tym, że „przeglądarka może nie być jeszcze całkiem gotowa, ponieważ współbieżność” lub coś w oparciu o „każda linia jest zdarzeniem dodawanym z tyłu kolejki”.
jsfiddle dostarczone przez DVK rzeczywiście ilustruje problem, ale jego wyjaśnienie nie jest poprawna.
To, co dzieje się w jego kodzie, polega na tym, że najpierw dołącza moduł obsługi zdarzeń do
click
zdarzenia w sieci#do
przycisku.Następnie, kiedy faktycznie klikniesz przycisk,
message
tworzony jest a, odwołujący się do funkcji obsługi zdarzeń, która jest dodawana domessage queue
. Gdyevent loop
osiągnie ten komunikat, tworzyframe
stos na stosie, z wywołaniem funkcji do modułu obsługi zdarzeń kliknięcia w jsfiddle.I tu robi się ciekawie. Jesteśmy tak przyzwyczajeni do myślenia o JavaScript jako asynchronicznym, że mamy tendencję do przeoczania tego drobnego faktu: każda ramka musi zostać wykonana w całości, zanim będzie można wykonać następną ramkę . Brak współbieżności, ludzie.
Co to znaczy? Oznacza to, że ilekroć funkcja jest wywoływana z kolejki komunikatów, blokuje ona kolejkę, dopóki stos, który generuje, nie zostanie opróżniony. Lub, bardziej ogólnie, blokuje się, dopóki funkcja nie powróci. I blokuje wszystko , w tym operacje renderowania DOM, przewijanie i tak dalej. Jeśli chcesz potwierdzić, po prostu spróbuj zwiększyć czas trwania długiej operacji w skrzypcach (np. Uruchom pętlę zewnętrzną jeszcze 10 razy), a zauważysz, że podczas jej działania nie można przewijać strony. Jeśli działa wystarczająco długo, przeglądarka zapyta, czy chcesz zabić proces, ponieważ powoduje to brak reakcji strony. Ramka jest wykonywana, a pętla zdarzeń i kolejka komunikatów zostają zablokowane, dopóki się nie zakończy.
Dlaczego więc ten efekt uboczny tekstu się nie aktualizuje? Ponieważ podczas nie zmienił wartość elementu w DOM - można
console.log()
jego wartość natychmiast po zmianie go i zobaczyć, że nie zostały zmienione (co pokazuje, dlaczego wyjaśnienie DVK nie jest poprawna) - przeglądarka czeka na stosie wyczerpywać (on
funkcja modułu obsługi do zwrócenia), a zatem komunikat do końca, aby mógł w końcu przejść do wykonania komunikatu dodanego przez środowisko wykonawcze w odpowiedzi na naszą operację mutacji oraz w celu odzwierciedlenia tej mutacji w interfejsie użytkownika .Jest tak, ponieważ faktycznie czekamy na zakończenie działania kodu. Nie powiedzieliśmy: „ktoś to pobiera, a potem wywołuje tę funkcję z wynikami, dziękuję, a teraz już gotowe, więc wracam, rób cokolwiek teraz”, jak to zwykle robimy z naszym asynchronicznym skryptem JavaScript opartym na zdarzeniach. Wchodzimy w funkcję obsługi zdarzenia kliknięcia, aktualizujemy element DOM, wywołujemy inną funkcję, inna funkcja działa przez długi czas, a następnie wraca, następnie aktualizujemy ten sam element DOM, a następnie wracamy z funkcji początkowej, skutecznie opróżniając stos. A następnie przeglądarka może dostać się do następnej wiadomości w kolejce, które mogłyby równie dobrze być komunikat wygenerowany przez nas przez wywołanie niektóre wewnętrzne „on-DOM-mutacji” typu imprezy.
Interfejs przeglądarki nie może (lub zdecyduje się nie aktualizować), dopóki bieżąca ramka nie zostanie zakończona (funkcja powróciła). Osobiście uważam, że jest to raczej zgodne z projektem niż z ograniczeniami.
Dlaczego więc to
setTimeout
działa? Robi to, ponieważ skutecznie usuwa wywołanie długo działającej funkcji ze swojej ramki, planując wykonanie jej później wwindow
kontekście, aby sam mógł natychmiast powrócić i pozwolić kolejce komunikatów na przetwarzanie innych komunikatów. Pomysł polega na tym, że komunikat „przy aktualizacji” interfejsu użytkownika, który został wywołany przez nas w JavaScript podczas zmiany tekstu w DOM, jest teraz przed komunikatem w kolejce dla funkcji długo działającej, aby aktualizacja interfejsu użytkownika miała miejsce przed zablokowaniem przez długi czas.Zwróć uwagę, że a) długo działająca funkcja nadal blokuje wszystko podczas działania, i b) nie masz gwarancji, że aktualizacja interfejsu użytkownika faktycznie wyprzedza ją w kolejce komunikatów. W mojej przeglądarce Chrome z czerwca 2018 r. Wartość
0
„nie” rozwiązuje problemu, który pokazuje skrzypce - 10. Właściwie jestem trochę tym zdławiony, ponieważ wydaje mi się logiczne, że komunikat o aktualizacji interfejsu użytkownika powinien być w kolejce przed nim, ponieważ jego wyzwalacz jest wykonywany przed zaplanowaniem, aby długo działająca funkcja była uruchamiana „później”. Ale być może istnieją pewne optymalizacje w silniku V8, które mogą przeszkadzać, a może po prostu brakuje mi zrozumienia.Okej, więc jaki jest problem z używaniem
setTimeout
i jakie jest lepsze rozwiązanie dla tego konkretnego przypadku?Po pierwsze, problem z używaniem
setTimeout
w jakiejkolwiek procedurze obsługi zdarzeń w celu złagodzenia innego problemu jest podatny na bałagan z innym kodem. Oto prawdziwy przykład z mojej pracy:Kolega, w błędnym rozumieniu pętli zdarzeń, próbował „powiązać” JavaScript, używając
setTimeout 0
do renderowania kodu do renderowania szablonu . Nie ma go tutaj, by zapytać, ale mogę założyć, że być może wstawił liczniki czasu, aby zmierzyć szybkość renderowania (co byłoby natychmiastową natychmiastową funkcją powrotu) i stwierdził, że zastosowanie tego podejścia spowodowałoby niezwykle szybkie odpowiedzi z tej funkcji.Pierwszy problem jest oczywisty; nie możesz wątkować w javascript, więc nic nie wygrywasz, dodając zaciemnianie. Po drugie, teraz skutecznie oddzieliłeś renderowanie szablonu od stosu możliwych detektorów zdarzeń, które mogą oczekiwać, że ten sam szablon zostanie wyrenderowany, podczas gdy może nie być. Rzeczywiste zachowanie tej funkcji było teraz niedeterministyczne, podobnie jak - nieświadomie - każda funkcja, która by ją uruchomiła lub od niej zależała. Możesz zgadywać, ale nie możesz poprawnie zakodować jego zachowania.
„Poprawka” podczas pisania nowego modułu obsługi zdarzeń zależnego od jego logiki również miała użyć
setTimeout 0
. Ale to nie jest poprawka, trudno ją zrozumieć i nie jest fajnie debugować błędy spowodowane przez taki kod. Czasami nigdy nie ma problemu, innym razem konsekwentnie zawodzi, a potem znowu, czasami działa i psuje się sporadycznie, w zależności od aktualnej wydajności platformy i wszystkiego, co dzieje się w tym czasie. Właśnie dlatego osobiście odradzam korzystanie z tego hacka ( jest to hack i wszyscy powinniśmy wiedzieć, że tak jest), chyba że naprawdę wiesz, co robisz i jakie są konsekwencje.Ale co można zamiast tego zrobić? Cóż, jak sugeruje przywołany artykuł MDN, podziel pracę na wiele wiadomości (jeśli możesz), aby inne wiadomości umieszczone w kolejce mogły być przeplatane z twoją pracą i wykonywane podczas jej działania, lub użyj robota internetowego, który może działać w parze z twoją stroną i zwracaj wyniki po zakończeniu obliczeń.
Aha, a jeśli myślisz: „Cóż, czy nie mógłbym po prostu oddzwonić do funkcji długo działającej, aby uczynić ją asynchroniczną?”, To nie. Oddzwonienie nie czyni go asynchronicznym, nadal będzie musiał uruchomić długo działający kod przed jawnym wywołaniem oddzwonienia.
źródło
Inną czynnością jest przesunięcie wywołania funkcji na spód stosu, zapobiegając przepełnieniu stosu, jeśli rekurencyjnie wywołujesz funkcję. Ma to efekt
while
pętli, ale pozwala silnikowi JavaScript odpalać inne asynchroniczne timery.źródło
push the function invocation to the bottom of the stack
. Tostack
, o czym mówisz, jest niejasne. Najważniejsze jest to, gdzie dokładniesetTimeout
dodaje tę funkcję, koniec tego cyklu pętli lub sam początek następnego cyklu pętli.Dzwoniąc do setTimeout, dajesz stronie czas na reakcję na wszystko, co robi użytkownik. Jest to szczególnie przydatne w przypadku funkcji uruchamianych podczas ładowania strony.
źródło
Niektóre inne przypadki, w których setTimeout jest przydatny:
Chcesz podzielić długo działającą pętlę lub obliczenia na mniejsze elementy, aby przeglądarka nie „zamroziła” się i nie powiedziała „Skrypt na stronie jest zajęty”.
Chcesz wyłączyć przycisk przesyłania formularza po kliknięciu, ale jeśli wyłączysz przycisk w module obsługi onClick, formularz nie zostanie przesłany. setTimeout z czasem zerowym rozwiązuje problem, pozwalając na zakończenie zdarzenia, rozpoczęcie przesyłania formularza, a następnie można wyłączyć przycisk.
źródło
Odpowiedzi na temat pętli wykonania i renderowania DOM przed uzupełnieniem innego kodu są poprawne. Zero drugich przekroczeń limitu czasu w JavaScript pomaga uczynić kod pseudo-wielowątkowym, nawet jeśli tak nie jest.
Chcę dodać, że NAJLEPSZA wartość limitu czasu zero-sekund w przeglądarce dla wielu przeglądarek / platform między platformami wynosi w rzeczywistości około 20 milisekund zamiast 0 (zero), ponieważ wiele przeglądarek mobilnych nie może zarejestrować limitów czasu krótszych niż 20 milisekund z powodu ograniczeń zegara na układach AMD.
Ponadto długotrwałe procesy, które nie wymagają manipulacji DOM, powinny być teraz przesyłane do pracowników sieci Web, ponieważ zapewniają one prawdziwą wielowątkową obsługę JavaScript.
źródło
setTimout na 0 jest również bardzo użyteczny w przypadku konfiguracji odroczonej obietnicy, którą chcesz od razu zwrócić:
źródło
Problem polegał na tym, że próbujesz wykonać operację JavaScript na nieistniejącym elemencie. Element miał być jeszcze załadowany i
setTimeout()
daje więcej czasu na załadowanie elementu w następujący sposób:setTimeout()
powoduje, że zdarzenie jest asynchroniczne, dlatego jest wykonywane po całym kodzie synchronicznym, dając Twojemu elementowi więcej czasu na załadowanie. Asynchroniczne wywołania zwrotne, takie jak wywołanie zwrotne,setTimeout()
są umieszczane w kolejce zdarzeń i umieszczane na stosie przez pętlę zdarzeń gdy stos kodu synchronicznego jest pusty.setTimeout()
jest często nieco wyższa (4-10 ms w zależności od przeglądarki). Ten nieco dłuższy czas potrzebny do wykonaniasetTimeout()
wywołania zwrotnego jest spowodowany ilością „tyknięć” (gdy tyknięcie wypycha wywołanie zwrotne na stosie, jeśli stos jest pusty) w pętli zdarzeń. Ze względu na wydajność i żywotność baterii ilość tyknięć w pętli zdarzeń jest ograniczona do pewnej ilości mniejszej niż 1000 razy na sekundę.źródło
JavaScript jest aplikacją jednowątkową, więc nie pozwala na jednoczesne uruchamianie funkcji, więc do osiągnięcia tego celu stosuje się pętle zdarzeń. Więc dokładnie to, co robi setTimeout (fn, 0), to wkleja się do zadania, które jest wykonywane, gdy stos wywołań jest pusty. Wiem, że to wytłumaczenie jest dość nudne, dlatego polecam przejrzenie tego filmu, który pomoże ci sprawdzić, jak działa przeglądarka pod maską. Sprawdź ten film: - https://www.youtube.com/watch?time_continue=392&v=8aGhZQkoFbQ
źródło