async await代码输出——面试题
先甩一段代码到你脸上,来看看你的答案是什么?
例子1
来请开始你的表演,默默在自己的小本本上记下以下这段代码的输出答案。
// 说出下面代码的输出
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start')
async1();
new Promise(function (resolve) {
console.log('promise1');
resolve();
}).then(function () {
console.log('promise2');
});
console.log('script end')
例子1解析
首先公布答案:script start、async1 start、async2、promise1、script end、async1 end、promise2
怎么样?你写在小本本上的答案和这个一致么?如果一致,那么恭喜你已经拥有六成功力,剩下的两成成在后面考验,最后的两成在面试的时候考验。
答案解析:
- 浏览器首先执行主线程上的代码,前面两段代码是两个异步函数async1和async2的定义(不执行),接着遇到了
console.log('script start'),所以浏览器打印输出script start - 接下来浏览器遇到async1()函数的执行,所以浏览器线程进入到async1函数内并创建相应上下文。先是执行
console.log('async1 start')并输出async1 start。 - 然后遇到了
await关键字,此时浏览器暂停执行await下面的代码 - 浏览器线程进入到async2函数内部,打印输出
async2。 - 由于await后面返回是一个promise,所以可以理解为该promise会把await下面的代码
console.log('async1 end')通过promise.then()推入到微任务队列中。注意这里是微任务队列中的第一个任务 - 执行完
await后面的语句async2之后,async1函数会交出代码执行权,所以浏览器会去执行主线程上面的代码。此时就会去执行new Promise()并输出promise1。同时resolve()并将该promise的回调函数then()推入微任务队列中。注意,这里console.log('promise2');是微任务队列中的第二个任务 - 然后继续执行主线程代码
console.log('script end')并输出script end - 此时主线程上的代码执行完毕,开始执行微任务队列中的任务。此时你还记得微任务队列中有几个任务,他们的顺序是什么吗?没错,微任务队列中的第一个任务是async1函数中await后面的代码
console.log('async1 end');。所以此时会打印输出async1 end。 - 第一个微任务执行完后,执行第二个微任务
console.log('promise2');,所以浏览器打印输出promise2
上面的例子1,感觉怎么样?下面我们来看看例子2
老规矩,请再次开始你的表演,默默在自己的小本本上记下以下这段代码的输出答案。
// 说出下面代码的输出
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
return new Promise((resolve, reiejct) => {
resolve()
})
}
console.log('script start')
async1();
new Promise(function (resolve) {
console.log('promise1');
resolve();
}).then(function () {
console.log('promise2');
});
console.log('script end')
例子2解析
首先公布答案:script start、async1 start、async2、promise1、script end、promise2、async1 end
欸?好像哪里不一样?怎么和例子一种有点不一样。是不是发现,怎么这个例子中promise2、和 async1 end的输出顺序与上个例子刚好相反呢?
答案解析:
- 前面的执行输出与例子1一样,不过多讲述。主要区别就是最后
promise2、和 async1 end的输出差别,所以这里我主要讲在这个例子中为什么promise2要先于async1 end输出 - async2()是异步函数,所以它本身返回一个
promise1,而async2中有return关键字。且该关键字返回一个promise2且将作为promise1的参数。形象地可以表述为下面这段代码 - 所以浏览器会把上面的这段嵌套的
promise1和promise2代码推入微任务队列。注意,这是微任务中的第一个任务。 - 执行完
await后面的语句async2之后,async1函数会交出代码执行权,所以浏览器会去执行主线程上面的代码。此时就会去执行new Promise()并输出promise1。同时resolve()并将该promise的回调函数then()推入微任务队列中。注意,这里console.log('promise2');是微任务队列中的第二个任务 - 此时主线程上的代码执行完毕,开始执行微任务队列中的任务。此时你还记得微任务队列中有几个任务,他们的顺序是什么吗?没错,微任务队列中的第一个任务是async1函数中await后面那段嵌套了
promise1和promise2的代码。所以浏览器执行promise1.then(),在执行promise1.then()时又遇到promise2.then(),所以浏览器将promise2.then()放入微任务队列末尾成为第三个微任务。 - 第一个微任务执行完后,执行第二个微任务
console.log('promise2'),所以浏览器打印输出promise2。 - 第二个微任务执行完成后,开始执行第三个微任务
promise2.then(() => { console.log('async end') }),打印输出async end - 打完收工!!!
总结
- await关键字后面是promise,如果是一个数值a,浏览器也会把该数值包装成
promise.reslove(a)。 - await关键后面的语句执行完后会交出代码执行权给主线程上的代码。
- 遇到await就会暂停执行await下面的代码,你可以理解为await后面的promise将其后面的代码通过
promise.then()推入到微任务队列中。 - 当然,看懂了这两个例子,你可以去试一试其他例子,巩固一下自己的理解。如果有误,欢迎指正,定会虚心接受并改进,谢谢!!!