有人可以向我解释为什么在运行程序时收到的警报总是“aaa”后跟“bbb”吗?我希望它遵循以下步骤:
我启动程序
程序运行并到达 setTimeout 行。它设置计时器,并且该计时器将从该时间点开始在 5 秒内触发(而不是在程序完成时)。事件队列中还没有任何内容
然后,在 5 秒过去之前,我单击该文档。由于程序很忙并且仍在运行循环,它会将此点击事件的回调放入事件队列中。这是目前偶数队列中唯一的事件(之前设置的计时器尚未触发)
5 秒过去了,循环可能仍在运行,我们用 setTimeout 设置的计时器触发。这会将计时器回调“aaa()”作为要执行的第二个事件回调放入事件队列,紧随已在队列中的单击事件之后。
因此,当程序完成时,我希望它首先触发“bbb”,因为首先触发此事件(在 5 秒前单击),然后是“aaa”(setTimeout 事件触发),但我总是得到“aaa”,然后是“bbb” “, 这是为什么?
事件队列不是先进先出(先进先出)吗?它依赖于浏览器吗?为什么会这样?
function aaa() {
alert("aaa");
}
setTimeout(aaa, 5000);
document.onclick = function () {
alert("bbb");
}
for (i = 0; i < 1000000; i++) {
console.log(i);
}
JSFiddle 在这里http://jsfiddle.net/sQvYG/1/
编辑:除非我遗漏了一些东西(可能非常尴尬),否则上面的代码看起来与 John Resigs 博客文章在http://ejohn.org/blog/how-javascript-timers-work的详细解释非常相似/。完全相同的事情,计时器启动,点击发生并添加到队列中。计时器触发并添加到队列中。执行结束并首先执行单击处理程序,这就是我所期望的。任何人都可以解释为什么这对我来说没有以正确的顺序发生?
编辑:感谢 bfavaretto 提供了已接受的答案,并且非常友好地详细解释了队列,我开始明白没有一个,而是放置事件的多个“任务队列”。所以我上面最初描述的 5 个步骤实际上是这些:
STEP 1. (同理)我启动程序
第 2 步。(这是相同的)程序运行并到达 setTimeout 行。它设置计时器,并且该计时器将从该时间点开始在 5 秒内触发(而不是在程序完成时)。事件队列中还没有任何内容。
我也相信此时任务队列中还没有任何东西,正如我在 mozilla 开发页面https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/EventLoop上发现的那样:
“调用 setTimeout 将在作为第二个参数传递的时间之后向队列添加一条消息。”
我还在 Chrome 中验证了这一点,似乎回调在实际触发时被添加到任务队列中。
第 3 步。然后,在 5 秒过去之前,我单击文档。由于程序很忙并且仍在运行循环,因此它将将此单击事件的回调放入浏览器为此类事件提供的任务队列之一(鼠标单击,ui 任务队列)。这是目前此任务队列中的唯一事件。其他任务队列(例如超时)目前是空的。
第 4 步。 5 秒过去了,循环可能仍在运行,我们用setTimeout
火设置了计时器。这会将计时器回调“aaa()”放入浏览器专门为此类事件(超时)提供的任务队列中。所以现在我们有一个处理用户交互的任务队列,我们有一个回调在等待(第 3 点中我们点击的回调)。我们还有另一个处理超时的任务队列,我们还有一个回调在那里等待。这是aaa()
5 秒后刚刚触发的方法。
STEP 5. 所以当程序完成时,我们有两个任务队列,每个队列都有一个回调。现在,即使首先将单击放入其任务队列,也不能保证首先处理此任务队列(即用户交互或处理鼠标单击的东西)。这完全取决于浏览器的实现,这让我很头疼,直到 bfavaretto 在他的回答中解释它。
所以在这个例子中我只提到了两个任务队列,但是浏览器可能有其他的。当程序运行并且忙时,每种类型的事件都被放置在其类型的任务队列中。程序完成后,浏览器决定要处理哪些任务队列,从那里抓取回调,执行,一旦完成,从同一个或另一个任务队列中抓取一些东西等等。所以实际上没有办法知道接下来会处理哪个队列。然而,每个特定队列中的任务总是按顺序处理。
Ooff .. 现在这似乎是有道理的,Resig 的文章让我对事情是如何完成的有了总体了解,但事实证明还有更多内容,希望最终我能正确理解它。