杨乐乐
Never too late

Never too late

EventLoop 微任务与宏任务

Photo by Oliver Hale on Unsplash

EventLoop 微任务与宏任务

杨乐乐's photo
杨乐乐
·Nov 12, 2022·

2 min read

Subscribe to my newsletter and never miss my upcoming articles

Table of contents

  • 什么是 EventLoop (事件轮询)
  • 宏任务和微任务优先问题
  • EventLoop 的执行顺序
  • 代码示例

什么是 EventLoop (事件轮询)

事件轮询就是解决 JavaScript 单线程处理异步操作时的一些缺陷。让 JavaScript 做到既是单线程,又不会阻塞的核心机制。是用来协调各种事件、用户交互、脚本执行、UI 渲染、网络请求等的一种机制。

宏任务和微任务优先问题

在任务队列中注册的两种异步任务分为 宏任务、微任务。可以认为任务队列中只有一个微任务队列,但是可以有多个宏任务队列

  • 宏任务(macro Task)
    • setTimeout / setInterval
    • script(整体代码)
    • setImmediate(Node环境)
    • UI 渲染
    • requestAnimationFrame
    • ...
  • 微任务
    • Promise的then()、catch()、finally()里面的回调
    • process.nextTick(Node 环境)
    • ...

EventLoop 的执行顺序

image.png

按照我的理解,若不看第一个宏任务(script),从同步代码执行结束后开始。微任务会优先执行,等到所有的微任务执行完成,才会执行宏任务队列。但是要注意是否在同一级。

代码示例

例一

console.log('script start'); 

setTimeout(() => {
  console.log('北歌');
}, 1 * 2000);

Promise.resolve()
    .then(function() {
      console.log('promise1');
    })
    .then(function() { 
      console.log('promise2'); 
    });


async function foo() {
  await bar()
  console.log('async1 end')
}
foo()

function bar() {
  console.log('async2 end') 
}

async function errorFunc () {
  try {
    await Promise.reject('error!!!')
  } catch(e) {
    console.log(e)
  }
  console.log('async1');
  return Promise.resolve('async1 success')
}
errorFunc().then(res => console.log(res))

console.log('script end');

可以先试着写一下答案是什么

代码从上开始执行,先执行一个宏任务(script)

console.log('script start'); // 同步任务 宏0

setTimeout(() => {
  console.log('北歌'); // 宏任务 宏1-1
}, 1 * 2000);

Promise.resolve()
    .then(function() {
      console.log('promise1');// 微任务 微1-1
    })
    .then(function() { 
      console.log('promise2'); // 微2-1 微任务,属于微任务产生的微任务 得等到微1执行完,并且resolve了,才会注册这个微任务
    });


async function foo() {
  await bar() // promise的语法糖,也是同步任务
  console.log('async1 end') // 微任务,微1-2
}
foo()

function bar() {
  console.log('async2 end') // 同步任务 宏0
}

async function errorFunc () {
  try {
    await Promise.reject('error!!!') 
  } catch(e) {
    console.log(e)// 微任务,微1-3
  }
  console.log('async1'); // 微任务,微1-4
  return Promise.resolve('async1 success') // 微2-2 微任务,属于微任务产生的微任务,需要一次执行完
}
errorFunc().then(res => console.log(res))

console.log('script end'); // 同步任务 宏0

根据上述描述,执行的顺序是

script start => async2 end => script end => promise1 => async1 end => error!!! => 
async1 => promise2 => async1 success => 北歌

例二

console.log('1'); // 同步代码 宏0

setTimeout(() => {
  console.log('2'); // 宏1-1
  Promise.resolve().then(() => {
    console.log('3'); // 微任务,属宏任务产生的微任务,微1-3
  })
  new Promise((resolve) => {
    console.log('4'); // 宏1-2
    resolve();
  }).then(() => {
    console.log('5') // 微任务,属宏任务产生的微任务,微1-4
  })
})

Promise.reject().then(() => {
  console.log('13'); // 不执行
}, () => {
  console.log('14'); // 微任务,微1-1
})

new Promise((resolve) => {
  console.log('7'); // 同步任务 宏0
  resolve();
}).then(() => {
  console.log('8') // 微任务 微1-2
})

setTimeout(() => {
  console.log('9');// 宏1-3
  Promise.resolve().then(() => {
    console.log('10');// 微任务,属宏任务产生的微任务,微1-5
  })
  new Promise((resolve) => {
    console.log('11');// 宏1-4
    resolve();
  }).then(() => {
    console.log('12')// 微任务,属宏任务产生的微任务,微1-6
  })
})

执行的顺序是

1 -> 7 -> 14 -> 8 -> 2 -> 4 -> 3 -> 5 -> 9 -> 11 -> 10 -> 12

例三

new Promise((resolve, reject) => {
  console.log(1)
  resolve()
})
.then(() => {
  console.log(2)
  new Promise((resolve, reject) => {
      console.log(3)
      setTimeout(() => {
        reject();
      }, 3 * 1000);
      resolve()
  })
    .then(() => {
      console.log(4)
      new Promise((resolve, reject) => {
          console.log(5)
          resolve();
      })
        .then(() => {
          console.log(7)
        })
        .then(() => {
          console.log(9)
        })
    })
    .then(() => {
      console.log(8)
    })
})
.then(() => {
  console.log(6)
})

执行的顺序是

1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9
 
Share this