nodejs,事件轮询总结

宏任务

script,setTimeoout,setInterval,setlmmediate(node 独有),I/o,render渲染

微任务

process.nextTick(),promise

1,执行timers阶段,执行setTimeOut()和setInterval到期的calback

2,I/O callbacks: 上一轮循环中少数callback/Io会被延迟到这一阶段执行

3,idle,prepare:队列的移动紧内部实现

4,poll:执行I/O,适当的条件下会阻塞这个阶段

5,check:执行setlmmediate的calback

6,close calback:执行close 事件的calback

1,1 例子

浏览器和node执行顺序的区别

setTimeout(()=>{

console.log('timer1')

Promise.resolve().then(function() {

console.log('promise1')

})

}, 0)

setTimeout(()=>{

console.log('timer2')

Promise.resolve().then(function() {

console.log('promise2')

})

}, 0)

浏览器输出:

time1

promise1

time2

promise2

Node输出:

time1

time2

promise1

promise2

在这个例子中,Node的逻辑如下:

最初timer1和timer2就在timers阶段中。开始时首先进入timers阶段,执行timer1的回调函数,打印timer1,并将promise1.then回调放入microtask队列,同样的步骤执行timer2,打印timer2;

至此,timer阶段执行结束,event loop进入下一个阶段之前,执行microtask队列的所有任务,依次打印promise1、promise2。

例子2

setImmediate(() => {

console.log('timer1')

Promise.resolve().then(function () {

console.log('promise1')

})

})

setTimeout(() => {

console.log('timer2')

Promise.resolve().then(function () {

console.log('promise2')

})

}, 0)

Node输出:

timer1 timer2

promise1 或者 promise2

timer2 timer1

promise2 promise1

按理说setTimeout(fn,0)应该比setImmediate(fn)快,应该只有第二种结果,为什么会出现两种结果呢?

这是因为Node 做不到0毫秒,最少也需要1毫秒。实际执行的时候,进入事件循环以后,有可能到了1毫秒,也可能还没到1毫秒,取决于系统当时的状况。如果没到1毫秒,那么 timers 阶段就会跳过,进入 check 阶段,先执行setImmediate的回调函数。

另外,如果已经过了Timer阶段,那么setImmediate会比setTimeout更快,例如:

const fs = require('fs');

fs.readFile('test.js', () => {

setTimeout(() => console.log(1));

setImmediate(() => console.log(2));

});

上面代码会先进入 I/O callbacks 阶段,然后是 check 阶段,最后才是 timers 阶段。因此,setImmediate才会早于setTimeout执行。

3不同异步任务执行的快慢

setTimeout(() => console.log(1));

setImmediate(() => console.log(2));

Promise.resolve().then(() => console.log(3));

process.nextTick(() => console.log(4));

输出结果:4 3 1 2或者4 3 2 1

因为我们上文说过microTask会优于macroTask运行,所以先输出下面两个,而在Node中process.nextTick比Promise更加优先[3],所以4在3前。而根据我们之前所说的Node没有绝对意义上的0ms,所以1,2的顺序不固定。

4MicroTask队列与MacroTask队列

setTimeout(function () {

console.log(1);

},0);

console.log(2);

process.nextTick(() => {

console.log(3);

});

new Promise(function (resolve, rejected) {

console.log(4);

resolve()

}).then(res=>{

console.log(5);

})

setImmediate(function () {

console.log(6)

})

console.log('end');

Node输出:

2 4 end 3 5 1 6

这个例子来源于《JavaScript中的执行机制》。Promise的代码是同步代码,then和catch才是异步的,所以4要同步输出,然后Promise的then位于microTask中,优于其他位于macroTask队列中的任务,所以5会优于1,6输出,而Timer优于Check阶段,所以1,6。