记一道前端的执行顺序题

先直接上代码

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')

以下是根据本人理解得出的输出结果

script start
async1 start
async2
async1 end
promise1
script end
promise2
setTimeout

但是实际上的运行结果是

script start
async1 start
async2
promise1
script end
promise2
async1 end
setTimeout

先说说我的理解:
宏任务(macrotask)和微任务(microtask)之间的区别和执行先后不再赘述
编译时先忽略函数的声明,直接到console.log('script start')这句中,故先输出script start

1.setTimeout(...)内部方法进入到macrotask队列中中,等待程序空闲后再执行,故先不输出
然后就应该执行async1()方法,所以先输出async1 start
2.然后执行await async2(),因为使用到了await,所以程序会被挂起,等待async2()返回
3.async2()执行输出async2并将控制权交还给async1方法
4.继续执行async1()输出async1 end
5.new Promise(...)中先执行Promise的构造方法,输出promise1并把resolve的执行函数压入microtask队列中
6.执行最后的console.log('script end'),输出script end
7.检查microtask,执行promise.then(...)里面的方法,输出promise2
8.检查macrotask,执行setTimeout(...)的回调,输出setTimeout

至此,程序执行完毕


然后我们对比下正确答案,主要的差别就是在async1 end的执行时机,也就是await async2();这条语句之后,程序的执行上下文究竟会去到哪
我们再重新理解一下
前面的输出不再重复再说,我们直接来到await async2()这一句上面去

我们先看看阮一峰大大的解释:

async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。

await async2()这一句,先输出了async2然后返回一个promise,并让出线程。程序往下执行
执行完console.log('script end')这一句后,程序查找microtask队列
首先搜查到的是await async2()这一句执行完后返回的promise,这个promise直接resolve,并将后面的语句放入到microtask。即这个时候才真正把console.log('async1 end')放入到microtask队列中
然后继续查找microtask队列,先执行new promise中的resolve,输出promise2
最后再搜索microtask队列,此时队列中只剩下console.log('async1 end'),输出async1 end.
至此,程序执行完毕,这就是最后正确答案的由来


以上是本人的拙见,如果有任何不正确的地方,欢迎读者指出纠正。

猜你喜欢

转载自blog.csdn.net/tangjiahao1996/article/details/87877647