2 node中的事件循环模型
2-1 一些属性
-
setInterval():----循环定时器
-
setTimeout();—延迟定时器
-
setImmendiate(); —立即执行函数
-
clearInterval();----清除循环定时器
-
clearTimeout();----清除演示定时器
-
clearImmendiate;----清除立即执行函数
/*立即执行函数*/
setImmediate(()=>{
console.log('我是异步执行的立即执行函数的回调函数');
})
console.log('我是同步执行的任务');
//我是同步执行的任务
// 我是异步执行的立即执行函数的回调函数
// 执行结果与异步执行结果并无两样
setImmendiate即立即执行函数,
和setTimeout(()=>{})类似,虽然是立即到了执行时间,但因为是异步回调,最后的结果仍然是等主线程上的任务清空时,在执行回调
2-2 循环模型
- 1 在node中有一种类似于js运行时的事件循环机制----事件轮询
- 2 在主线程上的代码被清空时,node的事件轮询机制就会开启
- 3 在轮询的过程中回调队列(消息队列)上若是有正在等待执行的回调函数,那么事件轮询将会对其进行相应的处理,若是没有回调函数正在等待执行,那么事件轮询将被挂起等待
node事件循环总共有六个阶段
-
第一个阶段:timers
定时器阶段
:1 开始计时 2 执行定时器的回调(setInterval,setTimeout)
-
第二个阶段:pending callbacks
系统阶段
-
第三个阶段:idle prepare
准备阶段
-
第四个阶段:poll(核心)
事件轮询阶段
1> 从队列里取出回调函数(除了定时器的回调),并同步执行,直到回调队列为空时,或者达到系统最大限度。
2> 如果回调队列为空时,继续判断是否有设置过setImmendiate
1) 有设置过则进入下一个check阶段,这个阶段是专门用于执行setimmediate所在设置的回调。
2) 没有则在此阶段停留,等待回调函数被插入回调队列。若这时有定时器的回调准备执行了,那么进入check阶段,目的:为了走第五个阶段,随后走第六个阶段,循环到第一阶段,执行定时器中的回调
- 第五个阶段:check —专门用于执行
setImmediate
所设置的回调
- 第六个阶段:close callbacks
关闭回调阶段
:
process.nextTick()函数
-
能在任意阶段优先执行
----除了主线程阶段 -
用于设置立即执行函数
-
和setImmendiate()类似
-
process.nextTick(()=>{});
/*立即执行函数*/ setImmediate(function () { console.log('我是异步执行的立即执行函数的回调函数'); }); console.log('我是同步执行的任务'); //我是同步执行的任务 // 我是异步执行的立即执行函数的回调函数 process.nextTick(function () { console.log('这是process.nextTick()函数钩子执行的回调函数,并且执行了'); }); console.log('我是同步执行的任务2'); // 我是同步执行的任务 // 我是同步执行的任务2 // 这是process.nextTick()函数钩子执行的回调函数,并且执行了 // 我是异步执行的立即执行函数的回调函数
注意:
setTimeout(()=>{console.log('setTimeout')});
setImmediate(()=>{console.log('setImmediate')});
按照事件轮询模型,这里的输出应当是
setImmediate
setTimeout
但是在实际执行过程中我们发现它们的执行顺序前后都不一定
原因:在执行的过程中,如果事件轮询速度快过于定时器计时速度,那么先执行setImmediate
,然后再执行setTimeout
.
若事件轮询的速度慢于计时器计时,那么先执行setTimeout
,在执行setImmediate
setTimeout(()=>{console.log('setTimeout')});
setImmediate(()=>{console.log('setImmediate')});
console.log('同步');
在代码里添加了同步任务,这里的执行结果又和之前的代码不一样了。执行顺序一直是 先setTimeout
,再setImmediate
原因:因为主线程在执行的过程,setTimeout(()=>{})完成,且因为主线程执行过程中轮询机制并未开始轮询,所以在第一阶段setTimeout(()=>{})就已经执行了。
实例
console.log('1');
setImmediate(() => {
console.log('2');
});
setTimeout(() => {
console.log('3');
})
setTimeout(() => {
console.log('4')
}, 1000);
console.log('5');
// 1
// 5
// 3
// 2
// 4
// 解释
// 先执行同步任务,定时器开始计时完成后挂起回调,异步任务推到消息队列等待主线程执行完成
// 输出 1 5
// 主线程执行完成,无同步任务,于是开启事件轮询
// 在第一阶段中发现定时器的回调待执行,于是执行定时器的回调,
// 输出 3
// 进入第二,三阶段
// 进入第四阶段,发现回调队列为空(1s回调的定时器时长中,事件轮询已经执行很多次),判断是否有
// setImmediate,发现有,则将其推入第六个阶段执行,
// 进入第六个阶段,发现上个阶段的setImmediate有回调函数,执行回调函数
// 输出 2
// 进入第一个阶段,发现没有定时器执行完毕返回回调(定时器未完成)
// 进入第2,3,4阶段
// 发现回调队列没有任务,此时事件轮询将在此处停留等待
// 若此时1s定时器回调函数准备执行了,那么事件轮询将进入check阶段,然后到第一个阶段执行定时器回调
// 输出 4
// 进入 2,3,4阶段,发现回调队列无任务,无setImmendiate,无准备执行的定时器回调,轮询将被停留