【JavaScript】ES2017:Async/Await执行机制及面试例题

前言

面试过程中,被问到一个题,因为忽略了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

猜你喜欢

转载自blog.csdn.net/qq_38987146/article/details/115084400
今日推荐