);
setTimeout(() => {
console.log(9)
}, 0);
console.log(10)
答案:1,4,10,5,6,7,2,3,9,8 你做对了吗? 如果还不明白的话对照图在走一次。
4. Node.JS事件循环流程简图
可以看出Node.JS的事件循环比浏览器端复杂很多。
5. NodeJS中的宏队列和微队列
5.1 事实上NodeJS中执行宏队列的回调任务有6个阶段,按如下方式依次执行:
timers阶段:这个阶段执行setTimeout和setInterval预定的callback。
I/O callback阶段:执行除了close事件的callbacks、被timers设定的callbacks、setImmediate()设定的callbacks这些之外的callbacks。
idle, prepare阶段:仅node内部使用。
poll阶段:获取新的I/O事件,适当的条件下node将阻塞在这里。
check阶段:执行setImmediate()设定的callbacks。
close callbacks阶段:执行socket.on('close', ....)这些callbacks。
5.2 其中宏队列有4个,各种类型的任务主要集中在以下四个队列之中:
Timers Queue
IO Callbacks Queue
Check Queue
Close Callbacks Queue
微队列主要有2个,不同的微任务放在不同的微队列中:
Next Tick Queue:是放置process.nextTick(callback)的回调任务的
Other Micro Queue:放置其他microtask,比如Promise等
6. Node的 EventLoop的具体流程:
执行全局Script的同步代码。
执行microtask微任务,先执行所有Next Tick Queue中的所有任务,再执行Other Microtask Queue中的所有任务。
执行macrotask宏任务,共6个阶段,从第1个阶段开始执行相应每一个阶段macrotask中的所有任务,注意,这里是所有每个阶段宏任务队列的所有任务,在浏览器的Event Loop中是只取宏队列的第一个任务出来执行,每一个阶段的macrotask任务执行完毕后,开始执行微任务,也就是步骤2。
Timers Queue -> 步骤2 -> I/O Queue -> 步骤2 -> Check Queue -> 步骤2 -> Close Callback Queue -> 步骤2 -> Timers Queue ......
重复1 - 4过程。
下面做一个小测试吧
console.log(0);
setTimeout(() => { // callback1
console.log(1);
setTimeout(() => { // callback2
console.log(2);
}, 0);
setImmediate(() => { // callback3
console.log(3);
process.nextTick(() => { // callback4
console.log(4);
}, 0);
setImmediate(() => { // callback5
console.log(5);
process.nextTick(() => { // callback6
console.log(6);
setTimeout(() => { // callback7
console.log(7);
process.nextTick(() => { // callback8
console.log(8);
}, 0);
process.nextTick(() => { // callback9
console.log(9);
console.log(10);
答案:0, 10, 9, 1, 4, 7, 8, 5, 6, 3, 2 。
7. 总结
1. 事件循环是 浏览器 和 Node 执行JS代码的核心机制,但浏览器 和 NodeJS事件循环的实现机制有些不同。
2. 浏览器事件循环有一个宏队列,一个微队列,且微队列在执行过程中一个接一个执行一直到队列为空,宏队列只取队首的一个任务放入执行栈执行,执行过后接着执行微队列,并构成循环。
3. NodeJS事件循环有四个宏队列,两个微队列,微队列执行方式和浏览器的类似,先执行Next Tick Queue所有任务,再执行Other Microtask Queue所有任务。 但宏队列执行时会依次执行队列中的每个任务直至队为空才开始再次执行微队列任务。
4. MacroTask包括: setTimeout、setInterval、 setImmediate(Node)、requestAnimation(浏览器)、IO、UI rendering
5. Microtask包括: process.nextTick(Node)、Promise、Object.observe、MutationObserver
8. 后记
学习事件循环会让我们对JS引擎执行代码流程有一个大概的了解,如果遇到任务执行顺序带来的问题,我们也能更快的解决。 同时也会让我们对异步编程有一个更深的认识。