为什么setTimeout的回调是在文档解析完之后执行的?

Why is setTimeout's callback executed after the document has been parsed?

我发现一些代码使用 setTimeout 来确保在解析文档后执行回调。

例如:

<!DOCTYPE html>
<html>
<head>
  <script>
    setTimeout(() => console.log(document.readyState));
  </script>
</head>
<body>

<!-- lots of HTML to parse, which will take a long time -->

</body>
</html>

这里先解析HTML,然后在控制台记录complete的文档readyState。

我的猜测是解析出现在回调之前的调用堆栈中或类似的东西,但我不明白这是如何工作的。我预计在解析和回调之间会有一场竞赛以先完成。

为什么回调在文档解析完成后执行?

因为setTimeout本质上是一个异步操作。即使没有指定实际超时(默认为 0),它也不会立即执行。它将任务添加到 "list of things to do later"。当 current 操作(在呈现页面时执行初始内联代码)完成时,事件循环将移动到该列表中的下一个。

它不能抢占当前操作,只有阻塞操作可以做到这一点,因为它们实际上是当前操作的一部分。添加到事件循环中的任何内容都将在以后处理。

顺便说一句,这也是为什么 setTimeoutsetInterval 不是 100% 准确的原因。正如您的代码意外演示的那样,超时实际上长于 0。您还可以将超时设置为 1000,然后立即有一个需要几秒钟来处理的阻塞循环。尽管有 1 秒的计时器,但在几秒的阻塞操作完成之前,事件循环无法绕过该操作。