前言
面试过程中,被问到一个题,因为忽略了await
出现后先执行async
外面的同步代码而出错,特此记录。题目详情:
async function async1(){
console.log('1');
await async2();
console.log('2');
}
async function async2(){
console.log('3');
}
console.log('4')
setTimeout(function(){
console.log('5')
}, 0);
async1();
new Promise(function(resolve){
console.log('6');
resolve();
}).then(function(){
console.log('7')
});
console.log('8')
下面详细说一下Async/Await关键字,在这之前需要了解js执行机制及其Event Loop,详情请移步【JavaScript】JS运行机制 Event Loop。
Async/Await
Async/Await
是提出其实是为了解决JS异步编程的问题。在这之前,ES2015提出了Promise
方案,通过链式调用让回调函数扁平化,减少了回调嵌套;ES6标准引入generator
,其配合执行器co
函数也可以解决回调函数嵌套问题。
为了更进一步简化异步编程语法,ES2017提出Async/Await
语法糖,其本质就是包装了Generator
生成器。Async/Await
示例:
async function main() {
let v1 = await new Promise(resolve => {
setTimeout(() => {
resolve('async excute 1')
}, 1000);
})
console.log(v1)
let v2 = await new Promise(resolve => {
setTimeout(() => {
resolve('async excute 2')
}, 1000);
})
console.log(v2)
}
// 执行
main()
正常来说,Async/Await
关键字是需要配合使用,且带async
关键字的函数返回值必是promise
对象。下面看一下这两个关键字后返回promise
对象与否的情况:
Async:
- 如果
async
方法中没有await
关键字,可以认为是一个普通的方法。async function main() { console.log('await') } console.log('next') main() // 输出:await, next
- 如果
async
函数返回非Promise
对象,那么返回值会自动用Promise.resolve()
包装。async function fn() { return 'foo' // 如果没有return,相当于return undefined } fn().then(data => { console.log(data) }) // 相当于 function fn1() { return new Promise(resolve => { resolve('foo') }) }
Await
- 如果
await
关键字后面是一个Promise
对象,那么相当于在Promise
对象中注入了then
处理方法接受异步操作返回值,开启一个微任务。async function main() { const v =await new Promise(resolve => { resolve('await') }) console.log(v) } main() console.log('next') // 输出: next, await
- 如果
await
关键字后面的Promise
对象中没有执行resolve
方法,就会导致Promise
一直处在pending
状态,无法执行then
方法,因此await后面的代码不会执行。async function main() { const v = await new Promise(resolve => { }) console.log(v) // console.log(v)不会执行 } main() console.log('next') // 输出: next
- 如果await关键字后面是一个非Promise的普通数据,那么其相当于执行Promise.resolve()。
async function main() { const v = await 'await' console.log(v) } main() console.log('next') // 输出: next, await // 相当于 async function main() { const v = await Promise.resolve('await') console.log(v) }
题解:
在理解了上面的概念后,我们回来再看那道面试题:
async function async1(){
console.log('1');
await async2();
console.log('2');
}
async function async2(){
console.log('3');
}
console.log('4')
setTimeout(function(){
console.log('5')
}, 0);
async1();
new Promise(function(resolve){
console.log('6');
resolve();
}).then(function(){
console.log('7')
});
console.log('8')
分析过程:
执行顺序 | 同步任务 | 微任务 | 宏任务 |
---|---|---|---|
async function async1() { console.log( ‘1’ ) await async2() console.log( ‘2’ ) } | |||
async function async2() { console.log( ‘3’ ) } | |||
console.log( ‘4’ ) | 1. 同步任务,打印4 | ||
setTimeout( function () { console.log( ‘5’ ) }, 0 ) | 2. 遇到定时器,放入宏任务队列,记为T1,此时宏任务队列(T1) | 8. 执行宏任务,打印5 | |
async1(); | 3. 遇到async1,开始执行,打印1; 遇到await async2开始执行async2,打印3; 因async2无return返回值默认为undefined,故返回值是Promise.resolve(undefined),则此时开启微任务,w1 | 6. 执行w1,打印2 | |
new Promise( function ( resolve ) { console.log( ‘6’ ) resolve(); } ).then( function () { console.log( ‘7’ ) } ) | 4. 执行promise的resolve,打印6,并把then加入微任务队列,记为w2 | 7. 执行w2,打印7 | |
console.log( ‘8’ ) | 5. 打印8 |
打印结果:4,1,3,6,8,2,7,5