先直接上代码
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.
至此,程序执行完毕,这就是最后正确答案的由来
以上是本人的拙见,如果有任何不正确的地方,欢迎读者指出纠正。