-
Notifications
You must be signed in to change notification settings - Fork 8
Open
Labels
Description
Node与浏览器Event Loop的差异
Node.js 运行机制
Node.js采用V8作为JS的解析引擎,而I/O处理方面使用了自己设计的libuv,libuv是一个基于事件驱动的跨平台抽象层,封装了不同操作系统一些底层特性,对外提供统一的API,Node.js内的事件循环机制也由其实现。
Node.js运行机制简化如下:
- V8引擎将JavaScript代码解析为机器码。
- 解析后的代码,调用Node API。
- libuv库负责Node API的执行。它将不同的任务分配给不同的线程,形成一个Event Loop(事件循环),以异步的方式将任务的执行结果返回给V8引擎。
- V8引擎再将结果返回给用户。
Node.js 事件循环
Node.js内的事件循环机制由libuv实现,分6个阶段反复进行。
┌───────────────────────────┐
┌─>│ timers │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │
│ └─────────────┬─────────────┘ ┌───────────────┐
│ ┌─────────────┴─────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └─────────────┬─────────────┘ │ data, etc. │
│ ┌─────────────┴─────────────┐ └───────────────┘
│ │ check │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │
└───────────────────────────┘
- timers 阶段:这个阶段执行timer(setTimeout、setInterval)的回调
- I/O callbacks 阶段:处理一些上一轮循环中的少数未执行的 I/O 回调
- idle, prepare 阶段:仅node内部使用
- poll 阶段:获取新的I/O事件, 适当的条件下node将阻塞在这里
- check 阶段:执行 setImmediate() 的回调
- close callbacks 阶段:执行 socket 的 close 事件回调
Node.js中也存在微任务队列,包括process.nextTick、promises等。
需要注意的两点:
process.nextTick优先级高于promises。- 事件循环每个阶段之间都会检查微任务队列并执行。
Node与浏览器Event Loop的差异
Node.js内,microtask 在事件循环的各个阶段之间执行;浏览器端,microtask 在事件循环的 macrotask 执行完之后执行。- Node 10及之前版本,timers阶段若有多个定时器回调,则全部执行之后再去检查微任务队列;而Node11及之后版本,则是执行一个宏任务就去检查微任务队列,跟浏览器端表现趋于一致。
setTimeout(()=>{
console.log('timer1')
Promise.resolve().then(() => console.log('promise1'))
}, 0)
setTimeout(()=>{
console.log('timer2')
Promise.resolve().then(() => console.log('promise2'))
}, 0)Node 10及之前版本打印顺序:timer1,timer2,promise1,promise2。
Node 11及之后版本打印顺序:timer1,promise1,timer2,promise2。