引言
无意中看到这样一道题,问最后结果是什么?由此分析并记录一下这道题的执行过程以及结果。
async function a1 () {
console.log('a1 start')
await a2()
console.log('a1 end')
}
async function a2 () {
console.log('a2')
}
console.log('script start')
setTimeout(() => {
console.log('setTimeout')
}, 0)
Promise.resolve().then(() => {
console.log('promise1')
})
a1()
let promise2 = new Promise((resolve) => {
resolve('promise2.then')
console.log('promise2')
})
promise2.then((res) => {
console.log(res)
Promise.resolve().then(() => {
console.log('promise3')
})
})
console.log('script end')
一轮分析
一、首先执行console.log('script start')
,这是毋庸置疑的。
二、接下来,有一个setTimeout方法,但setTimeout是一个macrotask(宏任务,即任务队列,当同步任务执行完了才会去读取任务队列),setTimeout要等同步任务和现有任务队列事件执行完才执行。所以 console.log('setTimeout')
被放到了任务队列的末尾。
三、Promise本身是一个同步的立即执行函数,但promise.then()是一个microtask(微任务),故console.log('promise1')
放入microtask的第一个。
四、执行a1(),由于async/await的本质是基于Promise的封装,而async返回的是Promise对象,故以下代码:
async function a1 () {
console.log('a1 start')
await a2()
console.log('a1 end')
}
等价于
async function a1 () {
console.log('a1 start');
Promise.resolve(a2,then( ()=>{
console.log('a1 end');
}) )
}
这样就可以知道console.log('a1 start');
被执行;然后执行async2(),即console.log('a2')
,然后啊a1()中的await后面的代码即console.log('a1 end')
就会被放到microtask的第二个。
五、接下来到下面这段代码了:
let promise2 = new Promise((resolve) => {
resolve('promise2.then')
console.log('promise2')
})
promise2.then((res) => {
console.log(res)
Promise.resolve().then(() => {
console.log('promise3')
})
})
可以看到,定义了一个Promise函数promise2,并且resolve带了参数promise2.then
,这个参数可以在then的回调函数中取得,即res。这里先执行console.log('promise2')
,然后再将then里的回调部分放入microtask的第三个。
六、最后执行console.log('script end')
二轮分析
通过一轮分析,我们暂时可以得到结果script start -> a1 start -> a2 -> promise2 -> script end -> …;
也就是说选择所有同步任务已经执行完毕,开始读取任务队列。
由于队列是先进先出的,故microtask按顺序执行(上面有备注第一个/第二个/第三个),即 promise1 -> a1 end -> …
第三个长这样,还得进行分析:
console.log(res)
Promise.resolve().then(() => {
console.log('promise3')
})
先执行console.log(res)
,其中res为resolve传的参数promise2.then
;然后又有一个promise.then,故放入microtask。但此时microtask没有其他任务,故执行console.log('promise3')
。
所以二轮结束后,结果应该是这样的:
script start -> a1 start -> a2 -> promise2 -> script end -> promise1 -> a1 end -> promise2.then -> promise3 -> …
三轮分析
任务队列中的microtask已经执行,且没有其他要执行的任务,故最后执行setTimeout,即console.log('setTimeout')
。
三轮结束后,得到完整结果:
script start ->
a1 start ->
a2 ->
promise2 ->
script end ->
promise1 ->
a1 end ->
promise2.then ->
promise3 ->
setTimeout
结语
这样类似的题目很多,但重要地是掌握其中的知识点。如果以上内容有表达不正确的地方欢迎指出并探讨!