关于Node.js的macrotask和microtask

在阅读Understanding the Node.js Event Loop这篇文章的时候,对文中一段实例代码的结果感到困扰。代码如下:

console.log('script start')

const interval = setInterval(() => {  
  console.log('setInterval')
}, 0)

setTimeout(() => {  
  console.log('setTimeout 1')
  Promise.resolve().then(() => {
    console.log('promise 3')
  }).then(() => {
    console.log('promise 4')
  }).then(() => {
    setTimeout(() => {
      console.log('setTimeout 2')
      Promise.resolve().then(() => {
        console.log('promise 5')
      }).then(() => {
        console.log('promise 6')
      }).then(() => {
        clearInterval(interval)
      })
    }, 0)
  })
}, 0)

Promise.resolve().then(() => {  
  console.log('promise 1')
}).then(() => {
  console.log('promise 2')
})

先说下这段代码运行的结果是:

script start
promise1
promise2
setInterval
setTimeout1
promise3
promise4
setInterval
setTimeout2
setInterval
promise5
promise6

对这个结果有两个疑问:
按照文中的解释,刚开始的时候:

setInterval 加入 macrotask 队列;
setTimeout 1 加入 macrotask 队列;
Promise.resolve 1 中,两个 then 加入 microtask 队列;
调用栈变空,microtask 执行。
Macrotask queue: setInterval, setTimeout 1

我的问题是,在其他文章中看到这样一段话:

在单次的迭代中,event loop首先检查macrotask队列,如果有一个macrotask等待执行,那么执行该任务。当该任务执行完毕后(或者macrotask队列为空),event loop继续执行microtask队列。如果microtask队列有等待执行的任务,那么event loop就一直取出任务执行知道microtask为空。

那么为什么运行的结果先是显示了promise1和promise2呢?这时候macrotask队列不是空的啊,为什么没有先显示setInterval?为什么刚开始直接从microtask队列开始执行?

第二个问题是在显示了promise3和promise4之后,文中解释这时队列的情况是:

microtask 队列为空,setInteval 回调执行,另一个 setInterval 加入 macrotask 队列,正好位于 setTimeout 2 之后;
Macrotask queue: setTimeout 2, setInteval
setTimeout 2 回调执行,promise 5 和 promise 6 加入 microtask 队列;

为什么结果是显示setInterval,然后setTimeout2,然后setInterval呢?我的理解是在显示了setTimeout2之后就应该紧接着执行microtask队列啊,也就是我觉得那部分应该是setInterval然后是setTimeout再然后是promise5,promise6,结果中为什么多了个setInterval?

阅读 5.8k
3 个回答

我觉的主要的点,在于立即执行和稍后执行 ,then中的代码显然是立即执行的,然后才轮到计时器中的代码

第一个问题:首先要理解eventloop,事件循环从检测macrotask queue开始,最开始的时候,队列里面是js主线程,因此先输出script start。随后有setInterval和setTimeout,这个两个都是macrotask的实现,因此会将这两个定时任务插入到macrotask queue中,接着是Promise,Promise是microtask的实现,因此插入到microtask queue中,至此,作为js主线程的macrotask执行完毕。而我们知道,macrotask队列在一次循环中只取出一个task执行,所以第一个js主线程的macrotask执行完之后,紧接着会判断microtask是否是空的,如果不是空的就会取出其中所有的task来执行,所以紧接着是logout promise1 promise2。。。后面的问题可以根据我前面说到的来类推了

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题