Event loop
浏览器中的Event loop
浏览器平台下一共有两个任务队列,一个是宏任务一个是微任务。
- 从上至下执行所有的同步代码
- 执行过程中将遇到的宏任务与微任务添加至相应的队列
- 同步代码执行完毕后,执行满足条件的微任务回调
- 微任务队列执行完毕后执行所有满足需求的宏任务回调
- 如此循环
- 注意:
每执行一个宏任务之后就会立刻检查微任务队列
setTimeout(() => {
console.log('s1')
Promise.resolve().then(() => {
console.log('p1')
})
Promise.resolve().then(() => {
console.log('p2')
})
})
setTimeout(() => {
console.log('s2')
Promise.resolve().then(() => {
console.log('p3')
})
Promise.resolve().then(() => {
console.log('p4')
})
})
打印结果:s1、p1、p2、s2、p3、p4
setTimeout(() => {
console.log('s1')
Promise.resolve().then(() => {
console.log('p2')
})
Promise.resolve().then(() => {
console.log('p3')
})
})
Promise.resolve().then(() => {
console.log('p1')
setTimeout(() => {
console.log('s2')
})
setTimeout(() => {
console.log('s3')
})
})
打印结果:p1、 s1、 p2、 p3、 s2、 s3
Nodejs中的Event loop
在浏览器平台下一共有两个任务队列,一个是宏任务一个是微任务。但在node下一共是有6个队列,如下
node中的6个队列说明
- timers宏任务队列: 执行setTimeout与setInterVal回调
- pending callbacks: 执行系统操作的回调,例如tcp、udp
- idle,prepare: 只在系统内部进行使用
- poll: 执行与I/O相关的回调
- check: 执行
setInnediate
中的回调 - close callbacks: 执行close事件的回调
Nodejs完整时间环
- 执行同步代码,将不同的任务添加至相应的队列
- 比如 遇到setTimeout会把任务添加到timers中,遇到I/O会把任务添加到poll中
- 所有同步代码执行后会去执行满足条件的微任务
- 所有微任务代码执行后会执行timer队列中满足的宏任务
- timer中的所有宏任务执行完成后就会依次切换队列
- 注意:
在完成队列切换之前会清空微任务代码
setTimeout(() => {
console.log('s1')
Promise.resolve().then(() => {
console.log('p1')
})
process.nextTick(() => {
console.log('t1')
})
})
Promise.resolve().then(() => {
console.log('p2')
})
console.log('start')
setTimeout(() => {
console.log('s2')
Promise.resolve().then(() => {
console.log('p3')
})
process.nextTick(() => {
console.log('t2')
})
})
console.log('end')
打印结果:start、end、p2、s1、t1、p1、s2、t2、p3
小结:
process.nextTick是微任务,它的执行顺序优先于Promise.then。 而且在旧版中node的执行顺序是需要等宏任务队列执行完再执行微任务的也就是start、end、p2、s1、s2、t1、t2、p1、p3。 在新版node中改为与浏览器保持一致了
Nodejs与浏览器Event loop的区别
- 任务队列数不同
- 浏览器中只有2个任务队列.宏任务与微任务
- Nodejs中除了微任务还有6个事件队列
- Nodejs微任务执行时机不同(新版node改为一致了)
- 二者都会在同步代码执行完毕后执行微任务
- 浏览器平台下每当一个宏任务执行完毕后就清空微任务
- nodejs平台在事件队列切换时会去清空微任务
- 微任务优先级不同
- 浏览器事件环中,微任务存放于事件队列,先进先出
- Nodejs中process.nextTick先于promise.then
Nodejs Event loop常见问题
setTimeout不管是在浏览器平台下还是在node平台下,它的第二个参数如果不传默认是0,而传0的话会有些不稳定因素,就会造成延时可能在setImmediate之后的情况。当我们不停的执行下面这段代码时就会偶现immdieate现被打印的情况。
setTimeout(() => {
console.log('timeout')
})
setImmediate(() => {
console.log('immdieate')
})
但是如果将同样的代码放到文件读取之后的回调中顺序又会是始终一致。如下
const fs = require('fs')
fs.readFile('./m1.js', () => {
setTimeout(() => {
console.log('timeout')
}, 0)
setImmediate(() => {
console.log('immdieate')
})
})
原因是fs.readFile是一个poll事件队列。这个队列的执行顺序如下