宏任务:计时器 ajax 读取文件等
微任务:promise等
执行顺序:
1、同步程序
2、process.nextTick(Node.js 环境)
3、微任务
4、宏任务
5、setImmediate
setImmediate(()=>{
console.log(1)
})
console.log(2)
setTimeout(()=>{console.log(3)},0)
setTimeout(()=>{console.log(4)},100)
console.log(5)
new Promise((resolve)=>{
console.log(6) // 内部是同步的
resolve()
}).then(()=>{
console.log(7) // then是放到任务队列里的 是异步的
})
// 输出顺序为 2、5、6、7、1、3、4
// 有时也会出现2、5、6、7、3、1、4
关于setTimeout和setImmediate
setTimeout和setImmediate在Node环境下执行是靠“随缘法则”的。
为什么会这样子呢?
这里我们要根据前面的那个事件循环不同阶段的图解来说明一下:
首先进入的是timers阶段,如果我们的机器性能一般,那么进入timers阶段,一毫秒已经过去了(setTimeout(fn, 0)等价于setTimeout(fn, 1)),那么setTimeout的回调会首先执行。
如果没有到一毫秒,那么在timers阶段的时候,下限时间没到,setTimeout回调不执行,事件循环来到了poll阶段,这个时候队列为空,此时有代码被setImmediate(),于是先执行了setImmediate()的回调函数,之后在下一个事件循环再执行setTimemout的回调函数。
而我们在执行代码的时候,进入timers的时间延迟其实是随机的,并不是确定的,所以会出现两个函数执行顺序随机的情况。
var fs = require('fs')
fs.readFile(__filename, () => {
setTimeout(() => {
console.log('timeout');
}, 0);
setImmediate(() => {
console.log('immediate');
});
});
这里setImmediate永远先于setTimeout执行
原因:fs.readFile的回调是在poll阶段执行的,当其回调执行完毕之后,poll队列为空,而setTimeout入了timers的队列,此时有代码被setImmediate(),于是事件循环先进入check阶段执行回调,之后在下一个事件循环再在timers阶段中执行有效回调。
- 如果两者都在主模块中调用,那么执行先后取决于进程性能,也就是随机。
- 如果两者都不在主模块调用(被一个异步操作包裹),那么setImmediate()的回调永远先执行。