node Event LOOP
是什么:
- 使用libuv的I/O 处理
- libuv是一个基于异步,事件驱动的跨平台抽象层
是node的新跨平台抽象层 - 核心是提供i/o的事件循环和异步回调
- 负责实现这种非阻塞行为,它使用应用程序线程调度挂起的任务。
- 在任务完成时发出一个事件,将需要处理的事件添加到事件队列。
- 对事件队列中的事件进行迭代,并安排何时执行其关联的回调函数
运行过程:
v8解析js代码->代码调用Node API->进入到libuv->不同任务分配到不同线程->以异步的方式将任务执行结果返回给V8引擎
Event Loop共分为6个阶段
来回执行(poll->check->close callback ->timer->i/o callbacks->idle,prepare)
具体阶段
- timers定时器 :执行 setTimeout 和 setInterval 到期的回调,并且由 poll 阶段控制的
- I/O pending callbacks待定回调:
执行延迟到下一个循环迭代的I/O回调
上一轮少数的callback会放在这一阶段执行,执行某些系统操作(例如TCP错误类型)的回调==】 - idle,prepare:系统内部使用
- poll轮询:
执行pengding callback
检索新的I/O事件
遍历回调对列并执行(I/o回调)执行I/O回调
除了关闭的回调函数,计时器和setImmediate调度的之外,其余均在此阻塞
处理轮询队列中的事件,
设定 timer 且poll 队列为空:判断是否有 timer 超时,超时则回到 timer 阶段执行回调 - check :
setImmediate()的回调会被加入 check 队列中,执行顺序在 poll 阶段之后,执行setImmediate的回调
如果队列为空,再执行setImmediate 回调。如果setImmediate 回调也没有(在check中),再等待回调被加入到队列尾部 - close callbacks:关闭socket.on(‘close’,…)等回调函数,执行close事件的callback
process.nextTick:每个阶段完成后,存在 nextTick 队列,就会清空队列中的所有回调函数,优先于其他 microtask 微任务执行
事件循环:在进程启动时,Node 便会创建一个类似于 while(true) 的循环,每执行一次循环体的过程我们成为 Tick。每个 Tick 的过程就是查看是否有事件待处理。如果有就取出事件及其相关的回调函数。然后进入下一个循环,如果不再有事件处理,就退出进程
代码执行过程
const { resolve } = require("path");
async function async1() {
console.log('async1 started');
await async2();
console.log('async end');
}
async function async2() {
console.log('async2');
}
console.log('script start.');
setTimeout(() => {
console.log('setTimeout0');
setTimeout(() => {
console.log('setTimeout1');
}, 0);
setImmediate(() => {
console.log('setImmediate');
})
}, 0);
process.nextTick(() => {
console.log('nextTick');
})
async1();
new Promise((resolve) => {
console.log('promise1');
resolve();
console.log('promise2');
}).then(() => {
console.log('promise.then')
});
console.log('script end.');
/**
* script start.
* async1 started
* async2
* promise1
* promise2
* script end.
* nextTick
* async end
* promise.then
* setTimeout0
* setImmediate
* setTimeout1
*/
解析
第一轮
宏任务1
输出
script start
async1 started: await中也是同步的代码
async2
promise1
promise2
script end
产生宏任务2:
setTimeout
产生微任务1:
process.nextTick
await下一行代码
promise.then
第二轮
微任务1
输出:
nextTick
async end
promise.then
第三轮
宏任务2
输出:
setTimeout0
产生宏任务3:
setTimeout1
产生微任务2:
setImmediate
第四轮
微任务2
setImmediate:setImmediate在定时器前
第五轮
宏任务3
输出:
setTimout1