一道面试题引发的Event Loop思考

1,002 阅读1分钟

话不多说,先上面试题

async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end')
}
async function async2() {
    console.log('async2');
}
console.log('script start');
setTimeout(function() {
    console.log('setTimeout');
}, 0);
async1();
new Promise(function(resolve) {
    console.log('promise1');
    resolve();
}).then(function() {
    console.log('promise2');
});
console.log('script end');

分析Eevent-Loop的问题时,我们将代码分成三块:同步代码,微任务队列,宏任务队列,所以从上到下:

  1. 同步代码:['script start', 'async1 start', 'async2', 'promise1', 'script end']
  2. 微任务:['async1 end', 'promise2']
  3. 宏任务:['setTimeout']

将输出结果依次从队列中取出:'script start', 'async1 start', 'async2', 'promise1', 'script end','async1 end', 'promise2','setTimeout'

难点

此问题的难点在于asyncawait, 那我们就来单独分析它

async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end')
}
async function async2() {
    console.log('async2');
}
console.log('script start');
async1();

// 同Generate函数一样,async函数返回一个Promise对象,可以使用then方法添加回调函数。
// 当函数执行时,一旦遇到await就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。
// 所以上面await async2()代码等价于
async function async1() {
  Promise.resolve(async2()).then(() => {
    console.log('async1 end');
  });
}

浏览器事件环

  • 首先执行同步代码,这属于宏任务(script,ui渲染属于宏任务)
  • 当执行完所有同步代码后,执行栈为空,查询是否有异步代码需要执行
  • 执行所有微任务
  • 当执行完所有微任务后,如有必要会渲染页面
  • 然后开始下一轮 Event Loop,执行宏任务中的异步代码

参考文章

  1. 前端轻语
  2. Advanced-Frontend / Daily-Interview-Question