什么是事件循环?
    先通过一段伪代码了解一下这个概念 :

    1. // eventLoop是一个用作队列的数组 //(先进,先出)
    2. var eventLoop = [ ];
    3. var event;
    4. //“永远”执行
    5. while (true) {
    6. // 一次tick
    7. if (eventLoop.length > 0) {
    8. // 拿到队列中的下一个事件
    9. event = eventLoop.shift();
    10. // 现在,执行下一个事件
    11. try {
    12. event();
    13. }
    14. catch (err) {
    15. reportError(err);
    16. }
    17. }
    18. }

    可以看到,有一个用 while 循环实现的持续运行的循环,循环的每一轮称为一个 tick。 对每个 tick 而言,如果在队列中有等待事件,那么就会从队列中摘下一个事件并执行。这些事件就是你的回调函数。
    一定要清楚,setTimeout(..) 并没有把回调函数挂在事件循环队列中。它所做的是设定一个定时器。当定时器到时后,环境会把你的回调函数放在事件循环中,这样,在未来某个时刻的 tick 会摘下并执行这个回调。
    如果这时候事件循环中已经有 20 个项目了会怎样呢?回调就会等待。它得排在其他项目后面——通常没有抢占式的方式支持直接将其排到队首。这也解释了为什么 setTimeout(..) 定时器的精度可能不高。大体说来,只能确保回调函数不会在指定的时间间隔之前运行,但可能会在那个时刻运行,也可能在那之后运行,要根据事件队列的状态而定。所以换句话说就是,程序通常分成了很多小块,在事件循环队列中一个接一个地执行。严格地说,和程序不直接相关的其他事件也可能会插入到队列中。