innerHTML是异步的吗?


101

我希望我不会自欺欺人,但是我试图理解这两行代码中正在发生的事情:

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

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

检出此Codepen以了解我的意思。

请注意,即使将alertsetTimeout(..., 0)没有帮助。似乎需要更多事件循环innerHTML才能实际更新页面。

编辑:

我忘了提我正在使用Chrome,但没有检查其他浏览器。看起来它仅在Chrome中可见。不过,我仍然很感兴趣为什么会这样。


4
这是典型的Chrome浏览器。
Trincot

2
@trincot谢谢!我忘了提我正在使用Chrome,在询问之前没有尝试其他浏览器。你有参考吗?
apieceofbart

16
更改DOM是同步的。实际上,在清除JavaScript堆栈后才呈现 DOM。developers.google.com/web/fundamentals/ Performance / rendering JavaScript>样式>布局>绘画>复合。(至少适用于Chrome浏览器。其他浏览器与此类似。)
凌晨

2
只是一个猜测:警报被阻止会阻止浏览器执行重新绘制步骤,因此,尽管DOM发生了变化,但尚未允许重新绘制;再次,只是一个猜测。
zzzzBov

2
@qbolec观看此视频:youtu.be/r8caVE_a5KQ
apieceofbart

Answers:


130

设置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内容之前,仅异步异步重绘页面,直到用户处理警报窗口为止。这并不意味着他们必须等待重绘发生。
JonasSchäfer17年

27

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

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

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


谢谢,我确实检查了一下。但是它必须是特定于Chrome的东西-它可以在其他浏览器中正常工作。还是他们等待页面重新绘制以移至下一行JavaScript的原因是他们的缺点?
apieceofbart

3
警报显示正确的文字吗?(text在我的示例中)这将回答您的问题,如果它是同步的。浏览器渲染与Javascript执行是苹果和橘子:)
d -_- b

它确实存在,但与问题无关
apieceofbart

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)不是每次都不能正常工作,请观看以下视频:youtu.be/r8caVE_a5KQ
apieceofbart
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.