innerHTML 是异步的吗?

IT技术 javascript html google-chrome dom
2021-03-08 06:15:54

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

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

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

查看此代码笔以了解我的意思。

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

编辑:

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

3个回答

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

(请记住,DOM 代表“文档对象模型”。它只是一个“模型”,一种数据的表示。用户在屏幕上看到的是该模型的外观图片。因此,更改模型不会立即发生更改图片 - 更新需要一些时间。)

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

Chrome 的管道如下所示:

在此处输入图片说明

如您所见,所有 JavaScript 都首先发生。然后页面被样式化、布局、绘制和合成——“渲染”。并非所有这些管道都会在每一帧执行。这取决于更改了哪些页面元素(如果有)以及它们需要如何重新呈现。

注意:alert()也是同步的并在 JavaScript 步骤期间执行,这就是为什么在您看到网页更改之前会出现警报对话框的原因。

您现在可能会问“等等,在管道中的‘JavaScript’步骤中到底运行了什么?我的所有代码每秒运行 60 次吗?” 答案是否定的,这要追溯到 JS 事件循环的工作原理。JS 代码只有在堆栈中时才会运行 - 来自事件侦听器、超时等。以前的视频(真的)。

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

@apieceofbart 其他浏览器也可以简单地异步重绘页面,同时暂停 javascript 内容,直到用户处理警报窗口。这并不意味着他们必须等待重绘发生。
2021-05-05 06:15:54
谢谢你的回答。我确实知道一些关于事件循环等的细节。这个管道非常有意义,但作为例子表明它在每个浏览器中都不同。如果其他浏览器在显示警报之前等待页面重新绘制,则意味着单击按钮和显示警报本身之间可能存在巨大延迟。稍后我会尝试使用它。
2021-05-06 06:15:54

是的,它是同步的,因为这是有效的(继续,在您的控制台中输入):

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

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

谢谢,我确实检查过。但它必须是 Chrome 特有的——它在其他浏览器中按预期工作。或者他们等待页面重新绘制以移动到下一行javascript是他们的缺陷?
2021-04-19 06:15:54
它有但与问题无关
2021-04-30 06:15:54
警报是否显示正确的文本?text在我的例子中)这将回答你是否同步的问题。浏览器渲染 vs. Javascript 执行是苹果和橘子 :)
2021-05-03 06:15:54
是的,这个问题显然是错误的,但我不知道该问什么。如果我知道正确的话,我可能会找到答案。我不是故意的,无论如何谢谢你的回答!
2021-05-11 06:15:54
您的问题实际上是“innerHTML 是异步的吗?”。如果该值可以在同步后立即使用,不是吗?我认为您的意思是询问更多关于页面渲染的问题,而不是 innerHTML 的同步质量。
2021-05-14 06:15:54

innerHTML财产实际并得到同步更新,但视觉重绘,这种变化会导致异步发生。

DOM 的可视化渲染在 Chrome 中是异步的,只有在当前 JavaScript 函数堆栈清除并且浏览器可以自由接受新事件后才会发生。其他浏览器可能使用单独的线程来处理 JavaScript 代码和浏览器呈现,或者当警报停止另一个事件的执行时,它们可能会让某些事件获得优先级。

您可以通过两种方式看到这一点:

  1. 如果您for(var i=0; i<1000000; i++) { }在警报之前添加,您已经给了浏览器足够的时间进行重绘,但它没有,因为函数堆栈尚未清除(add仍在运行)。

  2. 如果您alert通过 asynchronous延迟您setTimeout(function() { alert('random'); }, 1),重绘过程将在 setTimeout 延迟的函数之前进行。

    • 如果您使用 的超时,这将不起作用0,可能是因为 Chrome 将事件队列优先于0任何其他事件之前的超时(或至少在重绘事件之前)。
谢谢你的回答!请注意,即使setTimeout(func, 1)每次都不起作用,请查看此视频:youtu.be/r8caVE_a5KQ
2021-04-20 06:15:54