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

103

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

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

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

Проверить этот код чтобы понять, что я имею в виду.

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

РЕДАКТИРОВАТЬ:

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

за штуку
источник
4
Это типично для Chrome.
trincot
2
@trincot спасибо! Я забыл упомянуть, что использую Chrome и не пробовал другой браузер, прежде чем спросить. У вас есть ссылка?
apieceofbart
16
Изменение DOM происходит синхронно. Рендеринг DOM на самом деле происходит после очистки стека JavaScript. developers.google.com/web/fundamentals/performance/rendering JavaScript> Стиль> Макет> Рисование> Составной. (По крайней мере, для Chrome. Другие браузеры похожи.)
jered
2
просто предположение: блокировка предупреждений не позволяет браузеру перейти к шагу перерисовки, поэтому, хотя DOM изменилась, перерисовка еще не разрешена; опять же, просто предположение.
zzzzBov
2
@qbolec, посмотрите это видео: youtu.be/r8caVE_a5KQ
apieceofbart

Ответы:

132

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

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

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

Конвейер Chrome выглядит так:

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

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

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

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

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

одетый
источник
1
спасибо за ответ. Я знал некоторые подробности о цикле событий и т. Д. Этот конвейер имеет смысл, но, как показывают примеры, он не одинаков во всех браузерах. Если другие браузеры ждут, пока страница перерисовывается, прежде чем отобразить предупреждение, это означает, что между нажатием кнопки и отображением самого предупреждения может быть огромная задержка. Попробую поиграть позже.
apieceofbart
@apieceofbart Другие браузеры также могут просто асинхронно перерисовывать страницу, останавливая работу javascript, пока пользователь не коснется окна предупреждения. Это не значит, что им нужно ждать перекраски.
Йонас Шефер,
27

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

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

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

г -_- б
источник
Спасибо, я это проверил. Но это должно быть что-то особенное для Chrome - в других браузерах он работает должным образом. Или, может быть, их недостаток в том, что они ждут, пока страница перерисовывается, чтобы перейти к следующей строке javascript?
apieceofbart
3
В предупреждении отображается правильный текст? ( textв моем примере) Это ответит на ваш вопрос, синхронно ли это. Браузерный рендеринг против выполнения Javascript - яблоко и апельсины :)
d -_- b
Это имеет значение, но не имеет ничего общего с вопросом
каждый из них, барт,
1
Ваш вопрос буквально «Асинхронен ли innerHTML?». Если значение можно использовать сразу после того, как оно станет синхронным, нет? Я думаю, вы хотите больше узнать о рендеринге страниц, а не о синхронном качестве innerHTML.
d -_- b
8
Да, вопрос был явно неправильным, но я не знал, что спросить. Если бы я знал правильный, наверное, нашел бы ответ. Я не хотел быть злым, в любом случае спасибо за ответ!
apieceofbart
6

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

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

Вы можете увидеть это двумя способами:

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

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

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