Является ли innerHTML асинхронным?

Надеюсь, я не буду дурить сам, но я пытаюсь понять, что происходит в этих двух строках кода:

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

Я наблюдаю, что предупреждение отображается до того, как HTML был обновлен (или, может быть, он есть, но страница не была обновлена ​​/перекрашена/независимо)

Ознакомьтесь с этим codepen, чтобы понять, что я имею в виду.

Обратите внимание, что даже размещение alert в setTimeout(..., 0) не помогает. Похоже, для обновления страницы требуется больше циклов событий для innerHTML.

EDIT:

Я забыл упомянуть, что я использую Chrome и не проверяю другие браузеры. Похоже, это видно только в Chrome. Тем не менее меня все еще интересует, почему это происходит.

Ответ 1

Настройка innerHTML является синхронной, как и большинство изменений, которые вы можете внести в DOM. Тем не менее, создание веб-страницы - это совсем другая история.

(Помните, что DOM означает "Document Object Model".Это просто "модель", представление данных. То, что пользователь видит на своем экране, - это представление о том, как должна выглядеть эта модель. мгновенно изменить изображение - требуется некоторое время для обновления.)

Запуск JavaScript и отображение веб-страницы происходит фактически отдельно. Проще говоря, сначала выполняется весь JavaScript на странице (из цикла событий - проверьте это отличное видео для более подробной информации), а затем после этого браузер отображает любые изменения на веб-странице для просмотра пользователем. Вот почему "блокировка" - это большое дело - работающий с интенсивным вычислением код не позволяет браузеру пройти шаг "запустить JS" и шаг "сделать страницу", заставляя страницу замораживать или заикаться.

Конвейер Chrome выглядит следующим образом:

введите описание изображения здесь

Как вы можете видеть, сначала выполняется JavaScript. Затем страница оформляется, выкладывается, нарисована и составлена ​​- "рендер". Не весь этот конвейер будет выполнять каждый кадр. Это зависит от того, какие элементы страницы были изменены, если они есть, и как их нужно переизлучить.

Примечание: alert() также синхронно и выполняется во время действия JavaScript, поэтому перед тем, как вы увидите изменения на веб-странице, появится диалоговое окно предупреждения.

Теперь вы можете спросить: "Держись, что именно запускается на этом этапе" JavaScript "в конвейере? Все ли мой код работает 60 раз в секунду?" Ответ "нет", и он возвращается к тому, как работает цикл событий JS. JS-код запускается только в том случае, если он находится в стеке - от таких вещей, как прослушиватели событий, тайм-ауты, что угодно. Смотрите предыдущее видео (действительно).

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

Ответ 2

Да, это синхронно, потому что это работает (идите, введите его в консоль):

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

Причина, по которой вы видите предупреждение перед тем, как вы видите изменение страницы, заключается в том, что рендеринг браузера занимает больше времени и не так быстро, как ваш javascript, выполняющийся строкой.

Ответ 3

Фактическое свойство innerHTML обновляется синхронно, но визуальное перерисовка, вызванное этим изменением, происходит асинхронно.

Визуальный рендеринг DOM асинхронен в Chrome и не произойдет до тех пор, пока не удалит текущий стек функций JavaScript, и браузер не сможет принять новое событие. Другие браузеры могут использовать отдельные потоки для обработки кода JavaScript и браузера, или они могут позволить некоторым событиям получать приоритет, а предупреждение останавливает выполнение другого события.

Это можно увидеть двумя способами:

  • Если вы добавите for(var i=0; i<1000000; i++) { } перед своим предупреждением, вы предоставили браузеру много времени для перерисовки, но это не так, потому что стек функций не очищен (add все еще работает).

  • Если вы задержите alert с помощью асинхронного setTimeout(function() { alert('random'); }, 1), процесс перерисовки будет превзойти функцию, задержанную setTimeout.

    • Это не работает, если вы используете тайм-аут 0, возможно, потому, что Chrome предоставляет приоритет очереди событий 0 тайм-аутам перед любыми другими событиями (или, по крайней мере, перед событиями перерисовывания).