29

假设我这样做

setTimeout(foo, 0);

...

setTimeout(bar, 0);

我可以确定 foo 会在 bar 之前开始执行吗?如果我使用 1、10 或 100 作为 bar 的超时值而不是 0,会怎样?

简单的实验表明,在超时值相等的情况下,超时目标的执行顺序与 setTimeouts 本身相同,但是依赖这种行为是否安全?

4

5 回答 5

17

依赖这种行为是不安全的。我编写了一个测试脚本,它使用 setTimeout(..., 0) 调度了许多函数(它们反过来也调度了许多函数),并且调用函数的顺序并不总是与调用 setTimeout 的顺序(至少在我用来运行脚本的 Chrome 11 中)。

您可以在此处查看/运行脚本:http: //jsfiddle.net/se9Jn/ (该脚本使用 YUI 实现跨浏览器兼容性,但 Y.later 在内部使用 setTimeout)。

请注意,如果您只是运行脚本并盯着控制台,您可能不会看到有问题的命令。但是,如果启动脚本,切换到另一个选项卡,加载一些页面,然后返回测试页面,您应该会在控制台中看到有关无序回调的错误。

如果您需要有保证的排序,我建议您在前一个函数的末尾安排下一个函数:

setTimeout(foo, 0);

...

function foo() {

    ...

    setTimeout(bar, 0);
}
于 2011-05-13T22:19:07.283 回答
15

我通读了 Firefox 源代码,至少对于那个浏览器,有一个按非递减顺序指定的超时顺序保证。否则,所有赌注都被取消。

具体来说,如果在给定的执行上下文中为 n 设置超时,然后为 m 设置一个超时,其中 n <= m,则目标将按照设置超时的顺序执行。

但不要相信我的话。我相信窗口实现在 nsGlobalWindow.cpp 中,实际决定超时在执行队列中的位置的方法称为InsertTimeoutIntoList。看起来该方法向后遍历队列,寻找小于或等于给定超时的第一个超时,并在其后插入给定超时。

当然,当设置超时时,当前时钟时间会添加到超时值中。这就是为什么超时值必须按非递减顺序才能按顺序执行目标。

于 2009-11-21T20:31:49.117 回答
8

答案是:不,不能保证执行顺序。我偶尔会在 chrome 40 中看到非 FIFO 排序,尤其是当我在调试器中暂停而超时未决时。我在野外也有一次罕见的目击事件——这是对我调查的事件的唯一合理解释。

如果您需要保证执行顺序,setTimeout(callback, 0)您可以这样做:

var queue = [];
function handleQueue() {
    queue.shift()();
}
function queueCallback(callback) {
   queue.push(callback);
   setTimeout(handleQueue, 0);
}

现在,为了保证foobar您可以:

queueCallback(foo);
...
queueCallback(bar);
于 2015-01-30T13:22:44.673 回答
4

简而言之,不要指望它。看看这个

该图中有很多信息需要消化,但完全理解它会让您更好地了解异步 JavaScript 执行的工作原理。

于 2009-11-21T18:12:46.840 回答
2

延迟实际上有一个最小值,这在很大程度上取决于操作系统、浏览器、浏览器活动和计算机负载。我倾向于不低于 20 毫秒,因为我认为任何低于 20 毫秒的时间都没有任何区别。

当您放置两个相等的延迟时,并不一定意味着它将按该顺序发生。

如果你想确保它会按顺序完成,我倾向于做这样的事情:

setTimeout(function() { 
  foo();
  setTimeout(bar, 20)
}, 20);

这将始终保证秩序。

如果您使用的是 Firefox,为了确保您的 javascript 实际上是多线程的,您可能需要查看 WebWorker,它在较新的浏览器上受支持,但 IE 不支持。

于 2009-11-21T18:22:50.313 回答