innerHTML是异步的吗?

api*_*art 98 html javascript dom google-chrome

我希望我不会自欺欺人,但我想了解这两行代码中发生了什么:

document.body.innerHTML = 'something';
alert('something else');
Run Code Online (Sandbox Code Playgroud)

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

检查这个codepen看看我的意思.

请注意,即使将alertsetTimeout(..., 0)没有帮助.看起来innerHTML实际更新页面需要更多的事件循环.

编辑:

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

jer*_*red 130

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

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

运行JavaScript并呈现网页实际上是分开进行的.把它简单地说,首先是所有的JavaScript代码的页面运行(从事件循环-看看这个优秀的视频更多的细节),然后,浏览器呈现的网页进行任何更改用户看到的.这就是为什么"阻塞"是如此重要的原因 - 运行计算密集型代码会阻止浏览器通过"运行JS"步骤并进入"渲染页面"步骤,导致页面冻结或断断续续.

Chrome的管道如下所示:

在此输入图像描述

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

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

您现在可能会问:"坚持,管道中的'JavaScript'步骤究竟会运行什么?我的所有代码每秒运行60次吗?" 答案是"不",它可以追溯到JS事件循环的工作原理.JS代码只有在堆栈中才会运行 - 从事件监听器,超时等等.请参阅上一个视频(真的).

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


d-_*_*_-b 26

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

document.body.innerHTML = 'text';
alert(document.body.innerHTML);// you will see a 'text' alert
Run Code Online (Sandbox Code Playgroud)

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

  • 是的,问题显然是错的,但我不知道该问什么.如果我知道正确的那个可能会找到答案.我不是故意的意思,谢谢你的回答! (8认同)
  • 警报是否显示正确的文字?(我的例子中的`text`)这将回答你是否同步的问题.浏览器渲染与Javascript执行是苹果和橘子:) (3认同)

aps*_*ers 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在任何其他事件之前(或至少在重绘事件之前)将事件队列优先级提供给超时.