InternalHTML có không đồng bộ không?


101

Tôi hy vọng mình sẽ không tự lừa mình nhưng tôi đang cố gắng hiểu điều gì đang xảy ra trong hai dòng mã đó:

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

Những gì tôi đang quan sát là cảnh báo hiển thị trước khi HTML được cập nhật (hoặc có thể có nhưng trang chưa được làm mới / sơn lại / bất cứ điều gì)

Kiểm tra codepen này để xem tôi muốn nói gì.

Xin lưu ý rằng ngay cả việc đưa alertvào setTimeout(..., 0)cũng không giúp được gì. Có vẻ như cần nhiều vòng lặp sự kiện hơn innerHTMLđể thực sự cập nhật trang.

BIÊN TẬP:

Tôi quên đề cập rằng tôi đang sử dụng Chrome và không kiểm tra các trình duyệt khác. Có vẻ như nó chỉ hiển thị trong Chrome. Tuy nhiên tôi vẫn quan tâm tại sao điều đó lại xảy ra.


4
Điều này là điển hình cho Chrome.
trincot

2
@trincot cảm ơn! Tôi quên đề cập rằng tôi đang sử dụng Chrome và không thử trình duyệt khác trước khi hỏi. Bạn có bất kỳ tài liệu tham khảo?
apieceofbart

16
Thay đổi DOM là đồng bộ. Việc hiển thị DOM thực sự xảy ra sau khi ngăn xếp JavaScript đã bị xóa. Develop.google.com/web/fundamentals/performance/rendering JavaScript> Kiểu> Bố cục> Sơn> Tổng hợp. (Ít nhất là đối với Chrome. Các trình duyệt khác cũng tương tự.)
jered

2
chỉ là phỏng đoán: cảnh báo bị chặn ngăn trình duyệt đạt đến bước sơn lại, vì vậy mặc dù DOM đã thay đổi, nó vẫn chưa được phép sơn lại; một lần nữa, chỉ là một phỏng đoán.
zzzzBov

2
@qbolec hãy xem video này: youtu.be/r8caVE_a5KQ
apieceofbart Ngày

Câu trả lời:


130

Đặt innerHTML là đồng bộ, cũng như hầu hết các thay đổi bạn có thể thực hiện đối với DOM. Tuy nhiên, việc hiển thị trang web là một câu chuyện khác.

(Hãy nhớ rằng DOM là viết tắt của "Document Object Model". Nó chỉ là một "mô hình", một đại diện của dữ liệu. Những gì người dùng nhìn thấy trên màn hình của họ là hình ảnh về cách mà mô hình đó sẽ trông như thế nào. Vì vậy, việc thay đổi mô hình không phải ngay lập tức thay đổi hình ảnh - cần một thời gian để cập nhật.)

Việc chạy JavaScript và hiển thị trang web thực sự diễn ra riêng biệt. Nói một cách đơn giản, đầu tiên tất cả JavaScript trên trang đều chạy (từ vòng lặp sự kiện - hãy xem video xuất sắc này để xem chi tiết hơn) và sau đó sau khi mà trình duyệt làm cho bất kỳ thay đổi để trang web cho người dùng xem. Đây là lý do tại sao "chặn" lại là một vấn đề lớn như vậy - việc chạy mã chuyên sâu về tính toán sẽ ngăn trình duyệt vượt qua bước "chạy JS" và chuyển sang bước "hiển thị trang", khiến trang bị treo hoặc đơ.

Đường dẫn của Chrome trông giống như sau:

nhập mô tả hình ảnh ở đây

Như bạn có thể thấy, tất cả JavaScript xảy ra đầu tiên. Sau đó, trang được tạo kiểu, bố cục, vẽ và kết hợp - "kết xuất". Không phải tất cả đường ống này sẽ thực thi mọi khung hình. Nó phụ thuộc vào phần tử trang nào đã thay đổi, nếu có và cách chúng cần được hiển thị.

Lưu ý: alert()cũng đồng bộ và thực thi trong bước JavaScript, đó là lý do tại sao hộp thoại cảnh báo xuất hiện trước khi bạn thấy các thay đổi đối với trang web.

Bây giờ bạn có thể hỏi "Chờ đã, chính xác thì điều gì sẽ được chạy trong bước 'JavaScript' đó trong quy trình? Tất cả mã của tôi có chạy 60 lần mỗi giây không?" Câu trả lời là "không", và nó quay trở lại cách hoạt động của vòng lặp sự kiện JS. Mã JS chỉ chạy nếu nó nằm trong ngăn xếp - từ những thứ như trình nghe sự kiện, thời gian chờ, bất cứ điều gì. Xem video trước (thực sự).

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


1
cảm ơn vì đã trả lời. Tôi đã biết một số thông tin chi tiết về vòng lặp sự kiện, v.v. Đường ống này có ý nghĩa hoàn hảo nhưng như các ví dụ cho thấy nó không giống nhau trong mọi trình duyệt. Nếu các trình duyệt khác đợi trang sơn lại trước khi hiển thị cảnh báo, điều đó có nghĩa là có thể có sự chậm trễ lớn giữa việc nhấp vào nút và hiển thị cảnh báo. Tôi sẽ cố gắng chơi với nó sau.
apieceofbart

@apieceofbart Các trình duyệt khác cũng có thể sơn lại trang một cách không đồng bộ trong khi tạm dừng nội dung javascript cho đến khi người dùng xử lý cửa sổ cảnh báo. Nó không có nghĩa là họ phải đợi cho việc sơn lại diễn ra.
Jonas Schäfer

27

Có, nó là đồng bộ vì điều này hoạt động (hãy tiếp tục, nhập nó vào bảng điều khiển của bạn):

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

Lý do bạn thấy cảnh báo trước khi thấy trang thay đổi là do trình duyệt hiển thị mất nhiều thời gian hơn và không nhanh bằng việc thực thi javascript của bạn từng dòng một.


Cảm ơn, tôi đã kiểm tra điều đó. Nhưng nó phải là một cái gì đó cụ thể cho Chrome - nó hoạt động như mong đợi trong các trình duyệt khác. Hoặc có thể đó là lỗ hổng của họ khi họ đợi trang được sơn lại để chuyển sang dòng javascript tiếp theo?
apieceofbart

3
Cảnh báo có hiển thị văn bản chính xác không? ( texttrong ví dụ của tôi) Điều đó sẽ trả lời câu hỏi của bạn về việc nó có đồng bộ hay không. Kết xuất trình duyệt so với thực thi Javascript là táo và cam :)
d -_- b

Nó thực hiện nhưng không có gì để làm với các câu hỏi
apieceofbart

1
Câu hỏi của bạn theo nghĩa đen là "Có phải innerHTML không đồng bộ không?". Nếu giá trị có thể được sử dụng ngay sau khi nó được đồng bộ, không? Tôi nghĩ bạn muốn hỏi nhiều hơn về kết xuất trang chứ không phải chất lượng đồng bộ của innerHTML.
d -_- b

8
Vâng, câu hỏi rõ ràng là sai nhưng tôi không biết phải hỏi gì. Nếu tôi biết đúng nếu tôi có thể tìm thấy câu trả lời. Tôi không cố ý, dù sao cũng cảm ơn vì câu trả lời!
apieceofbart

6

Thực innerHTMLtế thuộc tính được cập nhật đồng bộ, nhưng bản vẽ lại trực quan mà thay đổi này gây ra xảy ra không đồng bộ.

Việc hiển thị trực quan DOM là không đồng bộ trong Chrome và sẽ không xảy ra cho đến khi ngăn xếp hàm JavaScript hiện tại bị xóa và trình duyệt có thể tự do chấp nhận một sự kiện mới. Các trình duyệt khác có thể sử dụng các chuỗi riêng biệt để xử lý mã JavaScript và hiển thị trình duyệt hoặc chúng có thể cho phép một số sự kiện được ưu tiên trong khi cảnh báo đang tạm dừng thực thi một sự kiện khác.

Bạn có thể thấy điều này theo hai cách:

  1. Nếu bạn thêm for(var i=0; i<1000000; i++) { }trước cảnh báo của mình, bạn đã cho trình duyệt nhiều thời gian để vẽ lại, nhưng nó chưa được, vì ngăn xếp hàm chưa xóa ( addvẫn đang chạy).

  2. Nếu bạn trì hoãn alertthông qua không đồng bộ setTimeout(function() { alert('random'); }, 1), quá trình vẽ lại sẽ đi trước chức năng bị trì hoãn bởi setTimeout.

    • Điều này không hoạt động nếu bạn sử dụng thời gian chờ 0, có thể do Chrome dành ưu tiên hàng đợi sự kiện cho 0thời gian chờ trước bất kỳ sự kiện nào khác (hoặc ít nhất là trước các sự kiện vẽ lại).

Cảm ơn vì đã trả lời! Xin lưu ý rằng thậm chí setTimeout(func, 1)không làm việc mọi thời gian, kiểm tra video này: youtu.be/r8caVE_a5KQ
apieceofbart
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.