innerHTML是异步的吗?

时间:2017-03-23 20:24:19

标签: javascript html google-chrome dom

我希望自己不会愚弄自己,但我试图了解这两行代码中发生的事情:

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

我观察到的是在HTML更新之前警报显示(或者可能已经更新,但页面没有被刷新/重新绘制/无论如何)

查看此codepen以了解我的意思。

请注意,即使将alert放入setTimeout(..., 0)也无济于事。看起来innerHTML实际更新页面需要更多的事件循环。

编辑:

我忘了提及我使用Chrome并没有检查其他浏览器。看起来它只在Chrome中可见。尽管如此,我仍然感兴趣为什么会发生这种情况。

3 个答案:

答案 0 :(得分:129)

设置innerHTML是同步的,您可以对DOM进行大多数更改。但是,呈现网页是另一回事。

(请记住,DOM代表"文档对象模型"它只是一个"模型",数据的表示。用户在屏幕上看到的是该模型应该如何看的图片。因此,更改模型并不会立即改变图片 - 需要一些时间来更新。)

运行JavaScript并呈现网页实际上是分开进行的。简单地说,首先运行页面上的所有JavaScript(从事件循环 - 检查this excellent video以获取更多详细信息)然后之后浏览器呈现对网页的任何更改供用户查看。这就是为什么"阻止"这是一个大问题 - 运行计算密集型代码阻止浏览器超越运行JS"步骤进入"渲染页面"步骤,导致页面冻结或口吃。

Chrome的管道如下所示:

enter image description here

正如您所看到的,所有JavaScript都是先发生的。然后页面被样式化,布局,绘制和合成 - "渲染"。并非所有此管道都将执行每一帧。这取决于更改的页面元素(如果有)以及如何重新呈现它们。

注意:alert()也是同步的,并且在JavaScript步骤中执行,这就是在您看到网页更改之前出现警告对话框的原因。

你现在可以问"坚持,在那个' JavaScript'进入管道?我的所有代码每秒运行60次吗?"答案是" no",它可以追溯到JS事件循环的工作原理。 JS代码只有在堆栈中才会运行 - 从事件监听器,超时等等。见previous video(真的)。

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

答案 1 :(得分:25)

是的,它是同步的,因为它可以工作(继续,在你的控制台中输入它):

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

在您看到页面更改之前看到警报的原因是浏览器渲染需要更多时间,并且不能逐行执行javascript。

答案 2 :(得分: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超时。 / LI>