Apakah innerHTML asynchronous?

102

Saya harap saya tidak akan mempermalukan diri saya sendiri, tetapi saya mencoba memahami apa yang terjadi dalam dua baris kode tersebut:

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

Apa yang saya amati adalah peringatan itu muncul sebelum HTML diperbarui (atau mungkin sudah tetapi halaman belum di-refresh / dicat ulang / apa pun)

Lihat codepen ini untuk melihat apa yang saya maksud.

Harap dicatat bahwa bahkan menempatkan alertdi setTimeout(..., 0)tidak membantu. Sepertinya perlu lebih banyak putaran acara untuk innerHTMLbenar-benar memperbarui laman.

EDIT:

Saya lupa menyebutkan bahwa saya menggunakan Chrome dan tidak memeriksa browser lain. Sepertinya itu hanya terlihat di Chrome. Meski demikian saya masih tertarik mengapa hal itu bisa terjadi.

masing-masing
sumber
4
Ini tipikal untuk Chrome.
trincot
2
@trincot terima kasih! Saya lupa menyebutkan bahwa saya menggunakan Chrome dan tidak mencoba browser lain sebelum bertanya. Apakah Anda punya referensi?
masing
16
Mengubah DOM itu sinkron. Rendering DOM sebenarnya terjadi setelah tumpukan JavaScript dibersihkan. developers.google.com/web/fundamentals/performance/rendering JavaScript> Gaya> Tata Letak> Cat> Komposit. (Setidaknya untuk Chrome. Browser lain serupa.)
dibuat
2
tebak saja: peringatan sedang memblokir mencegah browser mencapai langkah pengecatan ulang, jadi meskipun DOM telah berubah, ia belum diizinkan untuk mengecat ulang; sekali lagi, tebak saja.
zzzzBov
2
@qbolec periksa video ini: youtu.be/r8caVE_a5KQ
apieceofbart

Jawaban:

131

Menyetel innerHTML bersifat sinkron, seperti halnya sebagian besar perubahan yang dapat Anda lakukan pada DOM. Namun, merender halaman web adalah cerita yang berbeda.

(Ingat, DOM adalah singkatan dari "Document Object Model". Ini hanya "model", representasi data. Apa yang dilihat pengguna di layar mereka adalah gambaran bagaimana model itu seharusnya terlihat. Jadi, mengubah model tidak secara instan ubah gambar - perlu beberapa saat untuk memperbarui.)

Menjalankan JavaScript dan merender halaman web sebenarnya terjadi secara terpisah. Untuk meletakkannya menyederhanakan, pertama semua JavaScript pada halaman berjalan (dari loop acara - memeriksa video ini sangat baik untuk lebih detail) dan kemudian setelah itu browser merender perubahan apapun halaman web bagi pengguna untuk melihat. Inilah sebabnya mengapa "memblokir" adalah masalah besar - menjalankan kode yang intensif secara komputasi mencegah browser melewati langkah "jalankan JS" dan masuk ke langkah "render halaman", yang menyebabkan halaman macet atau tersendat.

Pipa Chrome terlihat seperti ini:

masukkan deskripsi gambar di sini

Seperti yang Anda lihat, semua JavaScript terjadi terlebih dahulu. Kemudian halaman diberi gaya, ditata, dicat, dan digabungkan - "render". Tidak semua pipeline ini akan mengeksekusi setiap frame. Itu tergantung pada elemen halaman apa yang diubah, jika ada, dan bagaimana mereka perlu dirender.

Catatan: alert()juga sinkron dan dijalankan selama langkah JavaScript, itulah sebabnya dialog peringatan muncul sebelum Anda melihat perubahan pada halaman web.

Sekarang Anda mungkin bertanya "Tunggu, apa sebenarnya yang dijalankan dalam langkah 'JavaScript' di pipeline? Apakah semua kode saya berjalan 60 kali per detik?" Jawabannya adalah "tidak", dan ini kembali ke cara kerja loop event JS. Kode JS hanya berjalan jika ada di tumpukan - dari hal-hal seperti pendengar acara, waktu tunggu, apa pun. Lihat video sebelumnya (sungguh).

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

jered
sumber
1
terima kasih telah menjawab Saya tahu beberapa detail tentang event loop, dll. Pipeline ini sangat masuk akal, tetapi sebagai contoh menunjukkan itu tidak sama di setiap browser. Jika browser lain menunggu halaman dicat ulang sebelum menampilkan peringatan, itu berarti mungkin ada penundaan besar antara mengklik tombol dan menampilkan peringatan itu sendiri. Saya akan mencoba bermain dengannya nanti.
masing
@apieceofbart Peramban lain mungkin juga mengecat ulang laman secara asinkron sambil menghentikan hal-hal javascript sampai pengguna berurusan dengan jendela peringatan. Bukan berarti mereka harus menunggu hingga pengecatan ulang dilakukan.
Jonas Schäfer
27

Ya, ini sinkron, karena ini berfungsi (lanjutkan, ketik di konsol Anda):

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

Alasan Anda melihat peringatan sebelum Anda melihat halaman berubah adalah karena rendering browser membutuhkan lebih banyak waktu dan tidak secepat javascript Anda mengeksekusi baris demi baris.

d -_- b
sumber
Terima kasih, saya sudah memeriksanya. Tetapi harus sesuatu yang spesifik untuk Chrome - berfungsi seperti yang diharapkan di browser lain. Atau mungkin itu kekurangan mereka bahwa mereka menunggu halaman dicat ulang untuk pindah ke baris javascript berikutnya?
masing
3
Apakah peringatan menampilkan teks yang benar? ( textdalam contoh saya) Itu akan menjawab pertanyaan Anda apakah itu sinkron. Rendering browser vs. eksekusi Javascript adalah apel dan jeruk :)
d -_- b
Itu tidak tetapi tidak ada hubungannya dengan pertanyaan
masing
1
Pertanyaan Anda secara harfiah adalah "Apakah innerHTML asynchronous?". Jika nilainya dapat digunakan segera setelah sinkron, bukan? Saya pikir Anda bermaksud bertanya lebih banyak tentang rendering halaman, bukan kualitas sinkron innerHTML.
d -_- b
8
Ya, pertanyaan itu jelas salah tapi saya tidak tahu harus bertanya apa. Jika saya tahu yang benar jika akan menemukan jawabannya mungkin. Saya tidak bermaksud jahat, terima kasih atas jawabannya!
masing
6

The innerHTMLproperti yang sebenarnya tidak diperbarui serentak, tetapi redraw visual yang perubahan ini menyebabkan terjadi asynchronous.

Rendering visual DOM tidak sinkron di Chrome dan tidak akan terjadi hingga tumpukan fungsi JavaScript saat ini dibersihkan dan browser bebas untuk menerima acara baru. Browser lain mungkin menggunakan utas terpisah untuk menangani kode JavaScript dan rendering browser, atau mereka mungkin membiarkan beberapa peristiwa mendapatkan prioritas sementara peringatan menghentikan pelaksanaan acara lain.

Anda dapat melihat ini dengan dua cara:

  1. Jika Anda menambahkan for(var i=0; i<1000000; i++) { }sebelum peringatan Anda, Anda telah memberi browser banyak waktu untuk menggambar ulang, tetapi belum, karena tumpukan fungsi belum dihapus ( addmasih berjalan).

  2. Jika Anda menunda alertmelalui asinkron setTimeout(function() { alert('random'); }, 1), proses penggambaran ulang akan melanjutkan fungsi yang ditunda oleh setTimeout.

    • Ini tidak berfungsi jika Anda menggunakan waktu tunggu 0, mungkin karena Chrome memberikan prioritas antrean acara ke 0waktu tunggu sebelum acara lain (atau setidaknya sebelum menggambar ulang acara).
apsillers
sumber
Terima kasih telah menjawab! Tolong jangan itu bahkan setTimeout(func, 1)tidak berfungsi setiap waktu, periksa video ini: youtu.be/r8caVE_a5KQ
apieceofbart