await async 执行顺序

await 与async 基本概念

这是Js异步的一种实现方式,一传统的promise不同,await让代码的书写更像我们传统的同步代码的方式,也就是这样:

let result = await fun();

而不像我们之前使用promise,需要调用then方法传入回调使用

async :
用在函数的开头,代表这个函数是一个异步函数

  • 其return的任何值都会被包装成promise
  • 在async声明的函数中可以使用await 调用其他异步函数

await:

  • 只能在async声明的异步函数中或者模块的外层作用域使用
  • 使用await调用异步函数,相当于调用promise的then方法
  • 只能通过try-catch 来处理异常

看代码

console.log("script start")
    new Promise((resolve) => {
            console.log("promise1")
            resolve('微任务1')
        })
        .then((data) => { //第一个Promise加入微任务列表
            console.log(data)
        })
    async function fun1() {
        console.log("fun1")
        let data = await fun2() //关键的地方,await后面的代码会阻塞 ,fun2执行完毕后,后面的代码类似于传入then()中的回调
        console.log(data)
        console.log("await堵塞")
    }

    setTimeout(() => {
        console.log("setTimeout 宏任务") //一个宏任务,加入宏任务队列
    }, 0)
    async function fun2() {
        console.log("fun2")
        // return "no promise"
        return new Promise((resolve, reject) => {
            resolve("fun2 微任务")
        })

        // return "await"

    }
    fun1() //代码开始执行
    new Promise((resolve) => {
            console.log("promise2")
            resolve("微任务2")
        })
        .then((data) => { //promise状态已经fulfilled,直接加入微任务队列
            console.log(data)
        })


    console.log("同步end") // 同步代码结束

上面代码,执行结果

script start
promise1
fun1
fun2
promise2
同步end
微任务1
微任务2
fun2 微任务
await堵塞
setTimeout 宏任务

这种情况是: await后面的异步函数return的是一个Promise

再看下面这种情况:

console.log("script start")
    new Promise((resolve) => {
            console.log("promise1")
            resolve('微任务1')
        })
        .then((data) => { //第一个Promise加入微任务列表
            console.log(data)
        })
    async function fun1() {
        console.log("fun1")
        let data = await fun2() //关键的地方,await后面的代码会阻塞 ,fun2执行完毕后,后面的代码类似于传入then()中的回调
        console.log(data)
        console.log("await堵塞")
    }

    setTimeout(() => {
        console.log("setTimeout 宏任务") //一个宏任务,加入宏任务队列
    }, 0)
    async function fun2() {
        console.log("fun2")
        return "no promise"
        // return new Promise((resolve, reject) => {
        //     resolve("fun2 微任务")
        // })

        // return "await"

    }
    fun1() //代码开始执行
    new Promise((resolve) => {
            console.log("promise2")
            resolve("微任务2")
        })
        .then((data) => { //promise状态已经fulfilled,直接加入微任务队列
            console.log(data)
        })


    console.log("同步end") // 同步代码结束

这种情况是,await后面的异步函数return的不是Promise

执行结果:

script start
promise1
fun1
fun2
promise2
同步end
微任务1
no promise
await堵塞
微任务2
setTimeout 宏任务

从结果上来看,await后面的异步函数return的是否是Promise,执行顺序的不同点在于await后面的代码执行是在微任务2之前还是之后执行,也就是微任务队列顺序的问题

当return的是Promise时:await后面的代码会在微任务2之后执行

当return的不是Promise时:await后面的代码会在微任务2之前执行

分析原因:
当return的不是promise时
这个也是我们通常的用法,因为async声明的异步函数会自动将返回包装成promise ,到调用fun2这个异步函数时,await就可以看做是使用then方法,会将回调函数(这里就是await后面的代码) 放入微任务队列,这里是排在微任务一之后,微任务二之前,所以这里await后面的代码会在微任务2之前执行

当return的是Promise时
这个用法会奇怪,但是面试题是这样写的😓
此时通过调试发现
在异步函数中return一个promise 返回的结果是一个 pending 状态的promise ,value为undefined
在这里插入图片描述
显然这个promise不是我们返回的promise,我们return的promise的状态肯定fulfilled,值为“no promise” 。但是此时我们data接收到的值仍然为“no promise”,通过浏览器调试,可以发现,如果在调试时没有去查看(打开)这个promise,最后代码全部执行完毕再去打开,会发现

在这里插入图片描述
这个promise状态又变正常了
说明在打印之后这个promise的状态发生了改变,现在问题就变成了这个promise到底是什么时候状态变成了fulfilled
说说async的实现原理,都说async是Generator的语法糖,那到底他们有什么关系呢?

async function fn(args) {
  // ...
}

// 等同于

function fn(args) {
  return spawn(function* () {
    // ...
  });
}

spawn函数是自动执行器,说简单一点,async会自动的调用next,并把return结果放在一个promise的resolve中

function spawn(genF) {
  return new Promise(function(resolve, reject) {
    const gen = genF();
    function step(nextF) {
      let next;
      try {
        next = nextF();
      } catch(e) {
        return reject(e);
      }
      if(next.done) {
        return resolve(next.value);
      }
      Promise.resolve(next.value).then(function(v) {
        step(function() { return gen.next(v); });
      }, function(e) {
        step(function() { return gen.throw(e); });
      });
    }
    step(function() { return gen.next(undefined); });
  });
}

改一下之前fun2的代码

 function fun2() {
    console.log('fun2');
    // return 'fun2 微任务';
    return new Promise((resolve, reject) => {
      resolve(new Promise((res) => {
          res('fun2 微任务');
        }));
    });
    // return new Promise((resolve, reject) => {
    //   resolve('fun2 微任务');
    // });

    // return "await"
}

没有用async模拟一下
在这里插入图片描述
结果与之前一样,执行顺序也一样
再次调试发现,当执行到微任务2结束之后下一个宏任务之前,此时fun2返回的promise状态会变为fulfilled
在这里插入图片描述
重点!!!
当resolve的参数是一个promise时,此时会将这个参数promise放入微任务队列(此时排在微任务1之后),而他的父级promise状态不会改变,等到微任务队列中执行到这个promise时,父promise的then方法绑定到子promise上,所以这就解释了上面的执行顺序的问题

附上最终版的代码

console.log('script start');
let fun2Data;
new Promise((resolve) => {
    console.log('promise1');
    resolve('微任务1');
}).then((data) => {
    //第一个Promise加入微任务列表

    console.log(data);
});
async function fun1() {
    console.log('fun1');
    fun2Data = fun2(); //关键的地方,await后面的代码会阻塞 ,fun2执行完毕后,后面的代码类似于传入then()中的回调
    console.log(fun2Data);
    let data = await fun2Data;
    console.log(data);
    console.log('await堵塞');
}

setTimeout(() => {
    console.log('setTimeout 宏任务'); //一个宏任务,加入宏任务队列
}, 0);
function fun2() {
    console.log('fun2');
    // return 'fun2 微任务';
    let innerPromise = new Promise((res) => {
        res('fun2 微任务');
    }).then((res) => {
        console.log("内部promise",res);
        return "内部promise返回"
    });
    let outPromise = new Promise((resolve, reject) => {
        resolve(innerPromise);
    });
    return outPromise
    // return new Promise((resolve, reject) => {
    //     setTimeout(() => {
    //         resolve('fun2 微任务');
    //     }, 5000);
    // });

    // return "await"
}

fun1(); //代码开始执行
new Promise((resolve) => {
    console.log('promise2');
    resolve('微任务2');
}).then((data) => {
    //promise状态已经fulfilled,直接加入微任务队列
    console.log(data);
});

console.log('同步end'); // 同步代码结束
/**
 * await 在执行到此微任务时才会将值返回
 * await后面不管是什么内容,都会加入微任务队列
 * 如果fun2不是async,返回是字符串和返回promise执行顺序一致,都按照微任务列表执行
 * 如果fun2为async,返回字符串同上,如果返回promise,此时任务会被放在微任务末尾,最后执行
 */
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值