什么是事件循环?
先通过一段伪代码了解一下这个概念 :
// eventLoop是一个用作队列的数组 //(先进,先出)
var eventLoop = [ ];
var event;
//“永远”执行
while (true) {
// 一次tick
if (eventLoop.length > 0) {
// 拿到队列中的下一个事件
event = eventLoop.shift();
// 现在,执行下一个事件
try {
event();
}
catch (err) {
reportError(err);
}
}
}
可以看到,有一个用 while
循环实现的持续运行的循环,循环的每一轮称为一个 tick
。 对每个 tick
而言,如果在队列中有等待事件,那么就会从队列中摘下一个事件并执行。这些事件就是你的回调函数。
一定要清楚,setTimeout(..)
并没有把回调函数挂在事件循环队列中。它所做的是设定一个定时器。当定时器到时后,环境会把你的回调函数放在事件循环中,这样,在未来某个时刻的 tick
会摘下并执行这个回调。
如果这时候事件循环中已经有 20 个项目了会怎样呢?回调就会等待。它得排在其他项目后面——通常没有抢占式的方式支持直接将其排到队首。这也解释了为什么 setTimeout(..)
定时器的精度可能不高。大体说来,只能确保回调函数不会在指定的时间间隔之前运行,但可能会在那个时刻运行,也可能在那之后运行,要根据事件队列的状态而定。所以换句话说就是,程序通常分成了很多小块,在事件循环队列中一个接一个地执行。严格地说,和程序不直接相关的其他事件也可能会插入到队列中。