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