假设我这样做
setTimeout(foo, 0);
...
setTimeout(bar, 0);
我可以确定 foo 会在 bar 之前开始执行吗?如果我使用 1、10 或 100 作为 bar 的超时值而不是 0,会怎样?
简单的实验表明,在超时值相等的情况下,超时目标的执行顺序与 setTimeouts 本身相同,但是依赖这种行为是否安全?
假设我这样做
setTimeout(foo, 0);
...
setTimeout(bar, 0);
我可以确定 foo 会在 bar 之前开始执行吗?如果我使用 1、10 或 100 作为 bar 的超时值而不是 0,会怎样?
简单的实验表明,在超时值相等的情况下,超时目标的执行顺序与 setTimeouts 本身相同,但是依赖这种行为是否安全?
依赖这种行为是不安全的。我编写了一个测试脚本,它使用 setTimeout(..., 0) 调度了许多函数(它们反过来也调度了许多函数),并且调用函数的顺序并不总是与调用 setTimeout 的顺序(至少在我用来运行脚本的 Chrome 11 中)。
您可以在此处查看/运行脚本:http: //jsfiddle.net/se9Jn/ (该脚本使用 YUI 实现跨浏览器兼容性,但 Y.later 在内部使用 setTimeout)。
请注意,如果您只是运行脚本并盯着控制台,您可能不会看到有问题的命令。但是,如果启动脚本,切换到另一个选项卡,加载一些页面,然后返回测试页面,您应该会在控制台中看到有关无序回调的错误。
如果您需要有保证的排序,我建议您在前一个函数的末尾安排下一个函数:
setTimeout(foo, 0);
...
function foo() {
...
setTimeout(bar, 0);
}
我通读了 Firefox 源代码,至少对于那个浏览器,有一个按非递减顺序指定的超时顺序保证。否则,所有赌注都被取消。
具体来说,如果在给定的执行上下文中为 n 设置超时,然后为 m 设置一个超时,其中 n <= m,则目标将按照设置超时的顺序执行。
但不要相信我的话。我相信窗口实现在 nsGlobalWindow.cpp 中,实际决定超时在执行队列中的位置的方法称为InsertTimeoutIntoList。看起来该方法向后遍历队列,寻找小于或等于给定超时的第一个超时,并在其后插入给定超时。
当然,当设置超时时,当前时钟时间会添加到超时值中。这就是为什么超时值必须按非递减顺序才能按顺序执行目标。
答案是:不,不能保证执行顺序。我偶尔会在 chrome 40 中看到非 FIFO 排序,尤其是当我在调试器中暂停而超时未决时。我在野外也有一次罕见的目击事件——这是对我调查的事件的唯一合理解释。
如果您需要保证执行顺序,setTimeout(callback, 0)
您可以这样做:
var queue = [];
function handleQueue() {
queue.shift()();
}
function queueCallback(callback) {
queue.push(callback);
setTimeout(handleQueue, 0);
}
现在,为了保证foo
,bar
您可以:
queueCallback(foo);
...
queueCallback(bar);
简而言之,不要指望它。看看这个。
该图中有很多信息需要消化,但完全理解它会让您更好地了解异步 JavaScript 执行的工作原理。
延迟实际上有一个最小值,这在很大程度上取决于操作系统、浏览器、浏览器活动和计算机负载。我倾向于不低于 20 毫秒,因为我认为任何低于 20 毫秒的时间都没有任何区别。
当您放置两个相等的延迟时,并不一定意味着它将按该顺序发生。
如果你想确保它会按顺序完成,我倾向于做这样的事情:
setTimeout(function() {
foo();
setTimeout(bar, 20)
}, 20);
这将始终保证秩序。
如果您使用的是 Firefox,为了确保您的 javascript 实际上是多线程的,您可能需要查看 WebWorker,它在较新的浏览器上受支持,但 IE 不支持。