node环境和浏览器环境,表现出来的事件循环状态,大致表现一致 惟一不一样的是:javascript
- JS引擎存在 monitoring process 进程,会持续不断的检查主线程执行为空,一旦为空,就会去 callback queue 中检查是否有等待被调用的函数。(只有宏任务和微任务两个队列)
- node 中是依靠 libuv 引擎实现,咱们书写的 js 代码有 V8 引擎分析后去调用对应的 nodeAPI ,这些 api 最后由 libuv 引擎驱动,在 libuv 引擎中有一套本身的模型,把不一样的事件放在不一样的队列中等待主线程执行。( 模型中有6种宏任务队列和1种微任务队列 )
// libuv引擎中的事件模型,在每一个模型后面都添加了一些说明
┌───────────────────────────────────────────────────────┐
┌─>│ timers │ setTimeout/setInterval的回调
│ └──────────┬────────────────────────────────────────────┘
│ ↓
│ ┌──────────┴────────────────────────────────────────────┐
│ │ pending callbacks │ 处理网络、流、tcp的错误回调
│ └──────────┬────────────────────────────────────────────┘
│ ↓
│ ┌──────────┴────────────────────────────────────────────┐
│ │ idle, prepare │ 只在node内部使用
│ └──────────┬────────────────────────────────────────────┘
│ ↓ ┌───────────────┐
│ ┌──────────┴────────────────────────────────────────────┐ │ incoming: │
│ │ poll │ 执行poll中的i/o队列,检查定时器是否到时 <------│ connections,
│ └──────────┬────────────────────────────────────────────┘ │ data, etc. │
│ ↓ └───────────────┘
│ ┌──────────┴────────────────────────────────────────────┐
│ │ check │ 存放setImmediate回调
│ └──────────┬────────────────────────────────────────────┘
│ ↓
│ ┌──────────┴────────────────────────────────────────────┐
└──┤ close callbacks │ 关闭的回调(socket.on('close')...)
└───────────────────────────────────────────────────────┘
复制代码
官方的event-loop-timers-and-nexttick更详细的说明java
setTimeout
和 setInterval
)setImmediate
回调这3个规定好的阶段setImmediate()
的回调会在这个阶段执行socket.on('close', ...)
process.nextTick
和 .then()
会在事件循环的阶段切换过程当中执行(function test() {
setTimeout(function () { console.log(4) }, 0);
new Promise(function (resolve, reject) {
console.log(1);
for (var i = 0; i < 10000; i++) {
i == 9999 && resolve();
}
console.log(2);
}).then(function () {
console.log(5);
});
console.log(3);
})();
// 这段代码是否是很熟悉
// 最终结果1,2,3,5,4 和 浏览器中效果一致
复制代码
和上篇博客 从一道执行题,了解浏览器中JS执行机制 中的代码同样 (⊙﹏⊙)bnode
console.log(1)
setTimeout(() => {
console.log(2)
new Promise(resolve => {
console.log(4)
resolve()
}).then(() => {
console.log(5)
})
})
new Promise(resolve => {
console.log(7)
resolve()
}).then(() => {
console.log(8)
})
setTimeout(() => {
console.log(9)
new Promise(resolve => {
console.log(11)
resolve()
}).then(() => {
console.log(12)
})
})
// 浏览器中的结果:一、七、八、二、4 , 五、九、十一、12
// Node 中的结果:一、七、八、二、4 , 九、十一、五、12
复制代码
解析以下:面试
- 在浏览器中
macro task
执行完成后,再次循环 宏任务 的回调队列以前,会优先处理micro中的任务。所以结果是: 一、七、八、二、四、五、九、十一、12- 在
Node
中有6个宏任务队列,事件循环首先进入 poll 阶段。进入 poll 阶段后查看是否有设定的 timers ( 定时器 )时间到达,若是有一个或多个时间到达, Event Loop 将会跳过正常的循环流程,直接从 timers 阶段执行,并执行 timers 回调队列,此时只有把 timers 阶段的回调队列执行完毕后。才会走下一个阶段,这也就是为何setTimeout
中有.then
,而没有被当即执行的缘由,当 timers 阶段的回调队列执行完毕后,切换到下一个阶段这个过程当中去触发 微任务(process.nextTick
和.then
) 。在阶段与阶段的切换之间。
setTimeout(function () {
console.log('setTimeout')
});
setImmediate(function () {
console.log('setImmediate')
});
复制代码
执行结果:(
setTimeout、setImmediate
) 或 (setImmediate、setTimeout
)api
为何?
setTimeout
在标准中默认的最小时间是4ms,若是开启node和执行node代码的时间小于4ms,那么代码解析完成后传入libuv
引擎,首先会进入 poll 阶段,此时查看设定的时间是否达到截止时间点,若是这个时间小于4ms( 没有达到 ),那么会走 check 阶段,会触发setImmediate
再触发setTimeout
。若是开启node和执行node代码时间大于等于4ms,那么就会先执行setTimeout
后执行setImmediate
浏览器
setImmediate(() => {
console.log('setImmediate1')
setTimeout(() => {
console.log('setTimeout1')
}, 0);
})
setTimeout(()=>{
process.nextTick(()=>console.log('nextTick'))
console.log('setTimeout2')
setImmediate(()=>{
console.log('setImmediate2')
})
},0);
复制代码
两种状况 ( nextTick执行的位置:是在队列切换时执行 )网络
- 若是
setImmediate
先执行:setImmediate一、setTimeout二、setTimeout一、nextTick、setImmediate2
- 若是
setTimeout
先执行:setTimeout二、nextTick、setImmediate一、setImmediate二、setTimeout1
- Immediate当即执行的意思,其其实是固定在
check
阶段才会被执行。这个直译的意义和process.nextTick
才是最匹配的。- node的开发者们也清楚这两个方法的命名上存在必定的混淆,他们表示不会把这两个方法的名字调换过来---由于有大量的node程序使用着这两个方法,调换命名所带来的好处与它的影响相比不值一提。
- 可使咱们对异步代码的执行顺序有清晰的认知( 重要的 )
- 推迟任务执行
- 面试
这些概念远比想象中的要重要异步
- 为何
new Promise
第一个参数是同步执行的 ?学习Promise && 简易实现Promise浏览器
中的 JS 执行机制是什么样子的?从一道执行题,了解浏览器中JS执行机制
附:这篇博客 也许 想表达 概念远比想象中的要重要 (⊙﹏⊙)bsocket