我有下一个代码
setTimeout(function() {
setTimeout(function() {
console.log('foo');
}, 50);
}, 100);
setTimeout(function() {
setTimeout(function() {
console.log('baz');
}, 100);
}, 50);

问题是输出是什么。但是在运行代码时,我在多次运行中获得了不同的结果 - 有时它是foo baz
,有时它是baz foo
。
所以有两个问题:
1)为什么我有不同的结果?
2)为什么有时候我会baz foo
?
P.S。有代码片段,但是使用代码段我总能得到相同的结果
P.P.S.如果它的环境特定 - 我使用Chrome(和FF),但问题仍然存在
P.P.S.可能的答案是关于使用console.log
,但是对于
var a = [];
setTimeout(function() {
setTimeout(function() {
a.push('foo');
}, 50);
}, 100);
setTimeout(function() {
setTimeout(function() {
a.push('baz');
}, 100);
}, 50);
setTimeout(function() { console.log(a); }, 300);

答案 0 :(得分:2)
setTimeout不会在您指定的精确时间之后计划事物 - 它是javascript引擎用来安排您的函数尽可能接近指定时间的近似值。通常,您不应该依赖超时来保证执行的顺序。假设您的超时可能在一段时间内,并且不要指望指定的时间是您的函数运行的确切时间。
阅读here了解详情。
答案 1 :(得分:2)
指定的超时是浏览器在执行函数之前应该等待的最短时间,而不是保证的时间。如果浏览器在计时器熄灭时忙于做其他事情,则该功能将被延迟。
因此,当您将计时器安排为50毫秒时,它可能直到53毫秒后才真正运行。然后它将在此之后设置另一个定时器100 ms,这是您启动后的153 ms。同时,设置为100 ms的定时器可以在101 ms内运行,然后将其第二个定时器设置为50 ms以后,即所有内容启动后的151 ms。在此示例中,它将打印foo bar
。
或者您可以获得不同的延迟,结果将是bar foo
。
如果您需要按特定顺序执行操作,则应在单个函数中按顺序运行它们,或者从第一个函数的回调中调用第二个函数,或者按特定顺序使用promise。取决于setTimeout
的精确毫秒时序是不可靠的。
答案 2 :(得分:0)
欢迎来到异步/事件驱动编程的世界!
了解javascript中的计时器(以及几乎所有语言中的通用计时功能)的关键之一是,它们的设计并不精确到tick。相反的是,该程序告诉操作系统/ js引擎“嘿,至少在这么多时间过去之后给我发送通知”。然而,操作系统/ js引擎可能需要数百,数千甚至数百万个任务来确定优先级和执行,因此它不能花费所有时间来观看时钟等待发出通知。因此,为了节省处理能力,将所有这些计时事件保存在队列中,并且只定期检查以查看已经过了多长时间以及事件是否已过期。
在您的特定情况下,由于触发了超时事件,您将创建一个超时事件。因此,如果初始事件稍微延迟,则会推迟开始时间,从而推迟第二个事件的到期。在你的foo / baz示例中,如果初始foo超时被延迟但是初始baz没有,那么baz回调将被添加到foo回调之前的事件队列中,并且你得到“baz foo”。
或者有时baz会被延迟而foo不会,或者有时也不会,或者有时他们都会。有太多的内容(甚至可能与您的脚本/代码/程序无关),以便能够预测或依赖于确切的执行顺序。这就是外卖,基本上所有的事件驱动编程都是一个很好的策略。