现阶段我所理解的事件循环

  • 2018-09-30
  • 28

流程如图:
event loop

浏览器与Nodejs比较,首先相同点:
微任务都是需要清空后再执行后续任务,也就是说,微任务中产生的其它微任务也会被加入到本轮循环的微任务队列末尾执行。

不同点:
1.Nodejs流程会复杂很多,微任务队列就分了两个:Next Tick Queue与Other Micro Queue,宏任务队列主要分了四个:Timers Queue,IO Callbacks Queue,Check Queue,Close Callbacks Queue

2.执行过程中,浏览器每轮事件循环只会从唯一的宏队列中取一个宏任务执行,而在node中存在多个宏队列,且每次执行完一个宏任务队列的所有任务,就会检查一遍两个微任务队列并执行,然后再执行下一个宏任务队列。
这点就造成了下面的这个代码在浏览器与nodejs中的结果不一样:

setTimeout(()=>{
    console.log(1);
    Promise.resolve().then(()=>console.log(2))
});
setTimeout(()=>console.log(3));
// 浏览器分别输出1,2,3
// Nodejs输出1,3,2   (ps:在shell中执行的话,先把代码缩到一行)

另外,由于setTimeout(fn,time),这个time就算被设置为0,也会被nodejs优化到其它值(一般是3),所以setTimeout与setImmediate如果同时写在同步代码中,他们的执行顺序是无法确定的。

试想一下setTimeout(fn1,0)与setImmediate(fn2)同时出现在同步代码中:
1.若在同步代码执行完成前,fn1被加入到了Timers Queue,那么由于Timers Queue先于Check Queue,这种情况下fn1会先于fn2被执行

2.若在同步代码执行完成前,fn1不能被加入到了Timers Queue(同步代码执行过快,小于3ms),那么由于Timers Queue为空,Check Queue中有fn2,这种情况下fn2会先于fn1被执行

那么如何确保二者的顺序不这么玄乎呢?

我们注意到Timers Queue与Check Queue之间还有个I/O Queue,如果我们将setTimeout与setImmediate同时写于I/O操作的回调中,则可以肯定上面的fn2会早于fn1执行。