NodeJS-EventLoop和浏览器区别
概述
和浏览器中一样 NodeJS 中也有 事件环(Event Loop)
,但是由于执行代码的 宿主环境
和 应用场景
不同,所以两者的事件环也有所不同:
- 扩展阅读:在 NodeJS 中使用
libuv
实现了Event Loop
- 源码地址:https://github.com/libuv/libuv
- 别看了 C/C++ 语言写的,你现在看不懂
NodeJS 事件环和浏览器事件环的区别
任务队列个数不同
- 浏览器事件环有
2
个事件队列(宏任务队列和微任务队列) - NodeJS 事件环有
6
个事件队列
微任务队列不同
- 浏览器事件环中有专门存储微任务的队列
- NodeJS 事件环中没有专门存储微任务的队列
微任务执行时机不同
- 浏览器事件环中每执行完一个宏任务都会去清空微任务队列
- NodeJS 事件环中只有同步代码执行完毕和其它队列之间切换的时候会去清空微任务队列
微任务优先级不同
- 浏览器事件环中如果多个微任务同时满足执行条件,采用先进先出
- NodeJS 事件环中如果多个微任务同时满足执行条件,会按照优先级执行
NodeJS 中的任务队列
┌───────────────────────┐
┌> │timers │执行setTimeout() 和 setInterval()中到期的callback
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │pending callbacks│执行系统操作的回调, 如:tcp, udp通信的错误callback
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │idle, prepare │只在内部使用
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │poll │执行与I/O相关的回调
│ (除了close回调、定时器回调和setImmediate()之外,几乎所有回调都执行);
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │check │执行setImmediate的callback
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
└─┤close callbacks │执行close事件的callback,例如socket.on("close",func)
└───────────────────────┘
┌───────────────────────┐
┌> │timers │执行setTimeout() 和 setInterval()中到期的callback
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │poll │执行与I/O相关的回调
│ (除了close回调、定时器回调和setImmediate()之外,几乎所有回调都执行);
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
└─┤check │执行setImmediate的callback
└───────────────────────┘
注意点
- 和浏览器不同的是没有宏任务队列和微任务队列的概念
- 宏任务被放到了不同的队列中,但是没有队列是存放微任务的队列
- 微任务会在执行完同步代码和队列切换的时候执行
什么时候切换队列?
- 当队列为空(已经执行完毕或者没有满足条件回到)
- 或者执行的回调函数数量到达系统设定的阈值时任务队列就会切换
在 NodeJS 中process.nextTick
微任务的优先级高于 Promise.resolve
微任务,验证代码如下:
Promise.resolve().then(function () {
console.log("Promise");
});
process.nextTick(function () {
console.log("process.nextTick1");
});
process.nextTick(function () {
console.log("process.nextTick2");
});
process.nextTick(function () {
console.log("process.nextTick3");
});
┌───────────────────────┐
│ 同步代码
└──────────┬────────────┘
│
│ <---- 满足条件微任务代码
│
┌──────────┴────────────┐
┌> │timers │执行setTimeout() 和 setInterval()中到期的callback
│ └──────────┬────────────┘
│ │
│ │ <---- 满足条件微任务代码
│ │
│ ┌──────────┴────────────┐
│ │poll │执行与I/O相关的回调
│ │ (除了close回调、定时器回调和setImmediate()之外,几乎所有回调都执行);
│ └──────────┬────────────┘
│ │
│ │ <---- 满足条件微任务代码
│ │
│ ┌──────────┴────────────┐
└─┤check │执行setImmediate的callback
└───────────────────────┘
注意点:
- 执行完 poll,会查看 check 队列是否有内容,有就切换到 check
- 如果 check 队列没有内容,就会查看 timers 是否有内容,有就切换到 timers
- 如果 check 队列和 timers 队列都没有内容,为了避免资源浪费就会阻塞在 poll