使用setTimeout执行多个脚本的顺序



我有一个空的html页面,里面有两个脚本:

<script src="./scripta.js"></script>
<script src="./scriptb.js"></script>

scripta.js:

console.log("from a")
setTimeout(() => {
console.log("from a timeout")
}, 0)

scriptb.js

console.log("from b")

现在,当我刷新页面时,我主要在控制台中看到这些日志:

from a
from b
from a timeout

根据我的理解,应该是这样的。

然而,在多次重新加载页面后,我也注意到了这些日志:

from a
from a timeout
from b

有人能解释或给我一个答案吗?为什么我会看到日志的第二个变体?谢谢

作为前言,请记住,当浏览器遇到纯ol脚本元素时,如<script>/* some script */</script><script src="someScript.js"></script>,浏览器会立即运行(或下载并运行(脚本,而如果<script>具有src=""属性,且具有deferasync属性,则脚本的执行将延迟。

  • (作为一个附加说明:我确实认为我们不能将defer与内联脚本一起使用是愚蠢的,尽管解决方法只是将代码封装在DOMContentLoaded事件侦听器回调中(

虽然setTimeout的规范确实保证回调的一些顺序,但它并不要求浏览器在使用setTimeout( func, 0 )的脚本完成后立即运行func

根据我对HTML+DOM规范的理解,以下两个场景应该被认为大致等效,并且应该始终具有相同的结果(假设所有内容都被缓存(,并具有以下输出:

foo
bar
myCallback

示例1

<html>
<head>
<script>
console.log("foo");
window.setTimeout( myCallback, 0 );
function myCallback() {
console.log("myCallback");
}
</script>
<script>
console.log("bar");
</script>
</head>
</html>

示例2

(其中scriptA.jsscriptB.js具有与其在前一示例中各自的内联<script>相同的脚本主体(

<html>
<head>
<script src="scriptA.js"></script>
<script src="scriptB.js"></script>
</head>
</html>

在您的情况下,会发生以下情况:

  1. 浏览器下载页面的HTML并开始解析。

  2. 当浏览器遇到第一个<script src="scriptA.js"></script>时,它将暂停处理HTML的其余部分,直到运行scriptA.js

  3. 浏览器运行scriptA.js,其中:

    3.1.呼叫console.log("from a")

    3.2.匿名函数(语法上位于setTimeout调用站点内的=>胖箭头函数(被添加到浏览器的任务队列中,以便在下一个事件周期执行,这通常在浏览器感觉时发生,即它是不确定的

    • CCD_ 24的CCD_;立即执行";也不意味着";同时执行";,相反,它具体地表示";在下一个事件周期中执行">
    • 浏览器中的JavaScript始终是单线程的:只有通过服务工作者才能进行并发执行

    3.3.scriptA.js完成后,浏览器可以根据运行时条件自由选择下一步操作(请继续阅读(。

  4. 优化,假设第二个<script>元素的HTML文本已经下载(几乎总是这样,因为并不是每个HTML元素都在自己的HTTP/TCP数据包中发送(,那么浏览器的HTML解析器将看到它需要下载并运行scriptB.js-如果scriptB.js已经缓存在浏览器中,则浏览器可以立即执行它,这意味着scriptB.js将在scriptA.jssetTimeout回调之前运行。

  5. 然而,如果scriptB.js尚未下载(缓存、解析并准备执行(,则浏览器知道必须首先等待,这会引入阻塞等待,但浏览器仍然可以使用该阻塞等待来运行任何挂起的JavaScript任务,包括setTimeout添加的任务。


最新更新