为主 JavaScript 线程安排的工作是 FIFO 处理的。这包括来自各种异步任务的回调,例如setTimeout
ajax 完成和事件处理程序。唯一的例外是,在主要流行环境(浏览器和 Node)中,本机的解析回调会Promise
跳队列(更准确地说,进入不同的更高优先级队列),有关详细信息,请参阅我的答案。
但抛开原生承诺解析回调:
但是所有三个函数都将被阻塞,直到 for 循环完成。在 18ms 时,for
循环结束,那么这些函数将按什么顺序被调用。(fn1,fn2,fn3) 或 (fn2,fn3,fn1)
您给出的时间setTimeout
是近似的,因为到了该时间时,JavaScript UI 线程可能正忙于做其他事情(如您所知);(新的)规范还需要最短时间,但实施的程度因实施而异。同样,您不能保证点击事件将在 6ms 排队,或者 ajax 完成将恰好在 7ms 发生。
如果该代码启动,并且浏览器精确地执行了 10 毫秒,并且click
事件恰好在 6 毫秒内排队,并且ajax 请求恰好在 7 毫秒完成,那么顺序将是:(fn2
处理click
程序),fn3
(ajax 完成),fn1
(setTimeout
),因为这是他们排队的顺序。
但请注意,这些时间非常紧迫。在实践中,我希望回调排队的顺序实际上是随机的,因为时间click
会有所不同,时间ajax
会有所不同,等等。
我认为这是一个更好的例子:
var start = +new Date();
// Queue a timed callback after 20ms
setTimeout(function() {
display("20ms callback");
}, 20);
// Queue a timed callback after 30ms
setTimeout(function() {
display("30ms callback");
}, 30);
// Queue a timed callback after 10ms
setTimeout(function() {
display("10ms callback");
}, 10);
// Busy-wait 40ms
display("Start of busy-wait");
var stop = +new Date() + 40;
while (+new Date() < stop) {
// Busy-wait
}
display("End of busy-wait");
function display(msg) {
var p = document.createElement('p');
var elapsed = String(+new Date() - start);
p.innerHTML = "+" + "00000".substr(elapsed.length - 5) + elapsed + ": " + msg;
document.body.appendChild(p);
}
输出的顺序将是两个循环消息,然后是 10ms 回调、20ms 回调和 30ms 回调,因为这是回调排队等待主 JavaScript 线程服务的顺序。例如:
+00001:忙等待开始
+00041:忙等待结束
+00043: 10ms 回调
+00044: 20ms 回调
+00044: 30ms 回调
...其中+
数字表示自脚本启动以来的毫秒数。