Czy innerHTML jest asynchroniczne?


103

Mam nadzieję, że nie zrobię z siebie głupka, ale staram się zrozumieć, co dzieje się w tych dwóch liniach kodu:

document.body.innerHTML = 'something';
alert('something else');

Zauważam, że alert pokazuje się przed aktualizacją HTML (a może tak, ale strona nie została odświeżona / odmalowana / cokolwiek)

Sprawdź ten kod, aby zobaczyć, co mam na myśli.

Należy pamiętać, że nawet wprowadzenie alertw setTimeout(..., 0)nie pomaga. Wygląda na to, że innerHTMLaby faktycznie zaktualizować stronę, potrzeba więcej pętli zdarzeń .

EDYTOWAĆ:

Zapomniałem wspomnieć, że używam Chrome i nie sprawdzałem innych przeglądarek. Wygląda na to, że jest widoczny tylko w Chrome. Niemniej jednak nadal jestem zainteresowany, dlaczego tak się dzieje.


4
Jest to typowe dla Chrome.
trincot

2
@trincot thanks! Zapomniałem wspomnieć, że używam Chrome i nie próbowałem innej przeglądarki przed zapytaniem. Czy masz jakieś referencje?
apieceofbart

16
Zmiana DOM jest synchroniczna. Renderowanie modelu DOM faktycznie ma miejsce po wyczyszczeniu stosu JavaScript. developers.google.com/web/fundamentals/performance/rendering JavaScript> Styl> Układ> Maluj> Złożony. (Przynajmniej dla Chrome. Inne przeglądarki są podobne.)
jered

2
tylko przypuszczenie: blokowanie ostrzeżenia uniemożliwia przeglądarce przejście do etapu ponownego malowania, więc chociaż DOM się zmienił, nie pozwolono mu jeszcze na przemalowanie; znowu tylko przypuszczenie.
zzzzBov

2
@qbolec sprawdź ten film: youtu.be/r8caVE_a5KQ
apieceofbart

Odpowiedzi:


132

Ustawienie innerHTML jest synchroniczne, podobnie jak większość zmian, które możesz wprowadzić w DOM. Jednak renderowanie strony internetowej to inna historia.

(Pamiętaj, DOM to skrót od „Document Object Model”. To tylko „model”, reprezentacja danych. To, co użytkownik widzi na ekranie, to obraz tego, jak ten model powinien wyglądać. Zatem zmiana modelu nie jest natychmiastowa zmień obrazek - aktualizacja zajmie trochę czasu.)

Uruchamianie JavaScript i renderowanie strony internetowej faktycznie odbywa się osobno. Mówiąc w uproszczeniu, przede wszystkim z JavaScript na stronie tras (z pętli zdarzeń - sprawdź to doskonały film dla bardziej szczegółowo), a następnie po że przeglądarka renderuje żadnych zmian na stronie, aby użytkownik mógł zobaczyć. Dlatego „blokowanie” to tak wielka sprawa - wykonanie kodu wymagającego dużej mocy obliczeniowej zapobiega przejściu przeglądarki przez krok „uruchom JS” i przejście do kroku „renderuj stronę”, co powoduje zawieszanie się lub zacinanie strony.

Potok Chrome wygląda następująco:

wprowadź opis obrazu tutaj

Jak widać, cały JavaScript odbywa się jako pierwszy. Następnie strona jest stylizowana, układana, malowana i komponowana - „renderowanie”. Nie cały ten potok wykona każdą klatkę. Zależy to od tego, jakie elementy strony uległy zmianie, jeśli w ogóle, i jak należy je ponownie wyrenderować.

Uwaga: alert() jest również synchroniczny i wykonywany podczas kroku JavaScript, dlatego okno dialogowe z ostrzeżeniem pojawia się, zanim zobaczysz zmiany na stronie internetowej.

Możesz teraz zapytać „Zaczekaj, co dokładnie jest uruchamiane w tym kroku 'JavaScript' w potoku? Czy cały mój kod działa 60 razy na sekundę?” Odpowiedź brzmi „nie” i wraca do sposobu działania pętli zdarzeń JS. Kod JS działa tylko wtedy, gdy znajduje się na stosie - z takich rzeczy, jak nasłuchiwanie zdarzeń, limity czasu itd. Zobacz poprzednie wideo (naprawdę).

https://developers.google.com/web/fundamentals/performance/rendering/


1
dziękuje za odpowiadanie. Znałem kilka szczegółów na temat pętli zdarzeń itp. Ten potok ma sens, ale jak pokazują przykłady, nie jest taki sam w każdej przeglądarce. Jeśli inne przeglądarki czekają na odświeżenie strony przed wyświetleniem alertu, oznacza to, że może wystąpić duże opóźnienie między kliknięciem przycisku a wyświetleniem samego alertu. Później spróbuję się tym bawić.
apieceofbart

@apieceofbart Inne przeglądarki mogą również po prostu asynchronicznie odświeżać stronę, zatrzymując działanie javascript do czasu, gdy użytkownik zajmie się oknem alertu. Nie oznacza to, że muszą czekać na przemalowanie.
Jonas Schäfer

27

Tak, jest synchroniczny, bo to działa (śmiało, wpisz to w konsoli):

document.body.innerHTML = 'text';
alert(document.body.innerHTML);// you will see a 'text' alert

Powodem, dla którego widzisz alert, zanim zobaczysz zmianę strony, jest to, że renderowanie przeglądarki zajmuje więcej czasu i nie jest tak szybkie, jak wykonywanie javascript wiersz po wierszu.


Dzięki, sprawdziłem to. Ale to musi być coś specyficznego dla Chrome - działa zgodnie z oczekiwaniami w innych przeglądarkach. A może to ich wada, że ​​czekają na przemalowanie strony, aby przejść do następnej linii javascript?
apieceofbart

3
Czy ostrzeżenie zawiera prawidłowy tekst? ( textw moim przykładzie) To odpowie na twoje pytanie, czy jest synchroniczny. Renderowanie przeglądarki a wykonanie JavaScript to jabłko i pomarańcze :)
d -_- b

To nie ma nic wspólnego z pytaniem
apieceofbart

1
Pytanie brzmi dosłownie „Czy innerHTML jest asynchroniczne?”. Jeśli wartości można użyć natychmiast po zsynchronizowaniu, nie? Myślę, że chcesz zapytać więcej o renderowanie stron, a nie o synchroniczną jakość innerHTML.
d -_- b

8
Tak, pytanie było ewidentnie błędne, ale nie wiedziałem o co zapytać. Gdybym znał właściwy, prawdopodobnie znalazłbym odpowiedź. Nie chciałem być podły, mimo wszystko dzięki za odpowiedź!
apieceofbart

6

Plik innerHTMLWłasność rzeczywista jest aktualizowane synchronicznie, ale wizualne przerysem że zmiana ta powoduje dzieje asynchronicznie.

Wizualne renderowanie DOM jest asynchroniczne w Chrome i nie nastąpi, dopóki bieżący stos funkcji JavaScript nie zostanie wyczyszczony, a przeglądarka nie będzie mogła zaakceptować nowego zdarzenia. Inne przeglądarki mogą używać oddzielnych wątków do obsługi kodu JavaScript i renderowania przeglądarki lub mogą pozwolić niektórym zdarzeniom uzyskać priorytet, podczas gdy alert wstrzymuje wykonanie innego zdarzenia.

Możesz to zobaczyć na dwa sposoby:

  1. Jeśli dodasz for(var i=0; i<1000000; i++) { }przed alertem, dałeś przeglądarce dużo czasu na wykonanie przerysowania, ale tak się nie stało, ponieważ stos funkcji nie został wyczyszczony ( addnadal działa).

  2. Jeśli opóźnisz alertza pomocą asynchronicznego setTimeout(function() { alert('random'); }, 1), proces przerysowania będzie kontynuowany przed funkcją opóźnioną przez setTimeout.

    • To nie działa, jeśli używasz limitu czasu z 0, prawdopodobnie dlatego, że Chrome nadaje priorytet kolejce zdarzeń 0przekroczeniu limitu czasu przed jakimikolwiek innymi zdarzeniami (lub przynajmniej przed zdarzeniami przerysowania).

Dziękuje za odpowiadanie! Proszę nie, że nawet setTimeout(func, 1)nie działa za każdym razem sprawdzić ten film: youtu.be/r8caVE_a5KQ
apieceofbart
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.