对JS中async与await理解

95 阅读2分钟

一、async

带async关键字的函数,是声明异步函数,返回值是promise对象,如果async关键字函数返回的不是promise,会自动用Promise.resolve()包装

        async function test() {
            return 10
        }
        console.log(test());

浏览器f12在控制台查看结果:

image.png

二、await

await会等待右侧的返回结果,这个结果可以是函数、对象等等任何数据类型。 程序运行过程中遇到await,会阻塞与await同一个async函数后面的代码,先执行外面的同步代码,同步代码执行完,再回到async内部,继续执行await后面的代码

分析一
        async function test() {
            await console.log('await');
            console.log('await后');
        }
        console.log('test运行前');
        test()
        console.log('test运行后');

结果:

image.png

程序运行,先打印‘test运行前’,然后调用test函数,在test函数中遇到了await关键字,就先运行await所在的同步语句,然后将test中await后面的语句挪到微任务队列、调动栈最后执行,然后按照同步顺序执行。

调用栈——微任务队列——宏任务关系图

image.png

分析二

        async function test() {
            async function fn() {
                await console.log('await');
                console.log('await后');
            }
            await fn()
            console.log('fn运行后');
        }

        console.log('test运行前');
        test()
        console.log('test运行后');

结果:

image.png

  1. 程序先打印‘test运行前’,然后进入test函数;
  2. 在test函数中遇到了await关键字,await所在语句为fn函数;
  3. 执行fn函数,在fn函数中遇到又遇到了await;
  4. 在fn中执行await所在语句,打印await
  5. 然后将await后面的语句挪到微任务队列最后;
  6. 出fn函数,将test中的await后面的语句挪到微任务队列
  7. 先执行fn中await后的语句,再执行test中await后面的语句

三、存在异步情况下的await

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')

结果:

image.png

  1. 先找同步,11、17、20、26行
  2. 11行打印'script start',17行是async1函数,进入函数
  3. 先执行async1中的同步,打印'async1 start'
  4. 遇到await关键字,执行await所在的语句——为async2函数
  5. 执行async2函数中的同步,打印'async2',
  6. 退出async2函数,将async1中await后的语句排到微任务队列
  7. 执行20行同步,打印'promise1'
  8. 执行26行同步,打印'script end'
  9. 找异步微任务,22~24为Promise微任务,将其挪入微任务队列
  10. 找宏任务,13~15位定时器宏任务,等浏览器倒计时结束,将定时器的回调函数放入宏任务队列
  11. 执行微任务队列的语句,打印'async1 end'
  12. 执行宏任务中的语句,打印'setTimeout'