理解setTimeout async promise执行顺序

2,865 阅读2分钟

下面是今日头条的前端面试题:

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() {
  //  setTimeout放入event-loop中的macro-tasks队列,暂不执行
  console.log("setTimeout");
}, 0);

async1();

new Promise(function(resolve) {
  console.log("promise1");
  resolve();
}).then(function() {
  console.log("promise end");
});
console.log("script end");

运行结果:

script start
async1 start
async2
promise1
script end
promise end
async1 end
setTimeout

image

第一步,执行同步代码:

async function async1() {
  console.log("async1 start"); // 同步代码2
  await async2(); // 调用async2(),async2()的返回值是promise,不执行promise的resolve,让出线程
  console.log("async1 end");
}
async function async2() {
  console.log("async2"); // 同步代码3
}
console.log("script start"); // 同步代码1

setTimeout(function() {
  // 异步 setTimeout放入event-loop中的macro-tasks队列,暂不执行
  console.log("setTimeout");
}, 0);

async1();

new Promise(function(resolve) {
  console.log("promise1"); // 同步代码4
  resolve();
}).then(function() {
  console.log("promise end"); // 不执行
});
console.log("script end"); // 同步代码5

  1. console.log("script start"); // 同步代码1这句代码毫无疑问是同步执行的 ;
  2. setTimeout()是异步任务,加入异步队列,不执行;
  3. 然后调用async1(),执行这个方法体内的同步函数,打印console.log("async1 start"); // 同步代码2
  4. 向下执行,遇到await关键字,调用async2(),执行同步代码打印console.log("async2"); // 同步代码3,让出线程。await是让出当前函数线程,交给函数外的代码执行;
  5. 线程跳出async1(),向下执行Promise(),执行里面的同步代码打印promise1resolve是异步函数,加入异步队列,此时继续执行同步函数,回到await关键字处,执行剩余代码;
  6. async2()是异步方法,默认返回promise,所以把返回的promise加入异步队列;
  7. 此时没有同步任务,就去执行异步任务,因为setTimeout()的优先级低于promise,所以会优先执行promise队列。
  8. 此时异步队列任务顺序: setTimeout()-new Promise().resolve()-async2().resolve(),setTimeout优先级低,所以先执行下一个,打印console.log("promise end");
  9. 继续执行异步任务,async2()执行完毕,同步await,这时候同步向下执行console.log("async1 end")
  10. 最后执行setTimeout()。