Skip to content

定义

  1. async 是异步的意思,await则可以理解为 async wait。所以可以理解async就是用来声明一个异步方法,而 await是用来等待异步方法执行
  2. async作为一个关键字放在函数前面,表示该函数是一个异步函数,异步函数意味着该函数的执行不会阻塞后面代码的执行;而 await 用于等待一个异步方法执行完成;
  3. 当函数内部执行到一个 await 语句的时候,如果语句返回一个 promise 对象,那么函数将会等待 promise 对象的状态变为 resolve 后再继续往下执行。并会阻塞该函数内后面的代码。
  4. 因此async/await的作用就是将异步逻辑,转化为同步的顺序来书写,并且这个函数可以自动执行。
  5. 为了优化 .then 链而开发出来的。

一、关于async

  1. async的用法,语法很简单,在函数前面加上async关键字,表示函数是异步的。它只有一个作用,他的调用会返回一个promise对象。
js
 async function demo1() {
     return '1'
 }
 async function demo2() {
    throw new Error('rejected');
 }
 async function demo3() {
      for (let index = 0; index < 3; index++) {
        
      }
 }
 async function demo4() {
    return '1'
 }
 console.log(demo1())
 console.log(demo2())
 console.log(demo3())
 demo4().then(val => {
    console.log(val)
 })
 console.log(2)

结果:

image-20240122165603258

发现 timeout() 函数虽然调用了,但是没打印1,而是返回了一个promise 对象,并且还有state和result。

如果async函数中有返回值,当调用该函数时,内部会调用Promise.resolve()把返回值转化成一个promise对象返回。

如果async内部抛出错误,那么会调用Promise.reject()返回一个promise对象。

如果想获取到async 函数的执行结果,调用promise的then 或 catch 即可。

如果async 函数执行完,返回的promise 没有注册回调函数,比如函数内部做了一次for 循环,你会发现函数的调用,就是执行了函数体,和普通函数没有区别,唯一的区别就是函数执行完会返回一个promise 对象。

二、关于await

async函数的执行会返回promise对象,并且把内部的值进行promise的封装。如果promise对象通过then或catch方法又注册了回调函数,async函数执行完以后,注册的回调函数就会放到异步队列中,等待执行。

如果只是async,和promise差不多,但有了await就不一样了,await关键字只能放到async函数里面,await是等待的意思,那么它等待什么呢?它后面跟着什么呢?

其实await不仅仅用于等Promise对象,还可以等任意表达式,所以await后面实际是可以接普通函数调用或者直接量的,不过我们更多的是放一个返回promise 对象的表达式。他等待的是promise对象执行完毕,并返回结果。

js
//所以下面这个示例完全可以正确运行
    function getSomething () {
      return 'something'
    }
    async function testAsync () {
      return Promise.resolve('hello async')
    }
    async function test () {
      const v1 = await getSomething()
      const v2 = await testAsync()
      console.log(v1, v2)
    }
    test()

如果await等到的不是一个Promise对象,那么await表达式的运算结果就是它等到的东西。

如果它等到的是一个Promise对象,await就忙起来了,它会阻塞函数后面的代码,等着Promise对象resolve,然后得到resolve的值,作为await表达式的运算结果。

**注意:**如果我们使用await,都知道需要给当前函数添加async标识。但是有一种特殊情况:

js
   function takeLongTime () {
      return new Promise(resolve => {
        setTimeout(() =>
          resolve('long_time_value'),
          1000
        )
      })
    }
     async function test () {
      let v = await takeLongTime()
      console.log(v, 'v')
    }
    test()

可以发现takeLongTime()没有加async,实际上takeLongTime () 本身就返回Promise对象,加不加async结果都一样。

await 优势在于处理 then 链,使代码看起来像同步代码一样,下面是实例应用

写一个async 函数,从而可以使用await 关键字, await 后面放置的就是返回promise对象的一个表达式,所以它后面可以写上 doubleAfter2seconds 函数的调用

js
// 2s 之后返回双倍的值
function doubleAfter2seconds (num) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve(num * 2)
        }, 2000)
   })
}

async function testResult() {
    let first = await doubleAfter2seconds(30);
    let second = await doubleAfter2seconds(50);
    let third = await doubleAfter2seconds(30);
    console.log(first + second + third);
}
testResult()

6秒后,控制台输出220, 我们可以看到,写异步代码就像写同步代码一样了,再也没有回调地域了。

**注意:**这里强调一下,当js引擎在等待promise.resolve的时候,他并没有真正的暂停工作,它可以处理其他的一些事情,如果我们在testResult函数后面继续执行其他代码,比如console.log一下,会发现console.log代码先执行。

三、案例

js
 async function async1() {
    console.log('async1 start')
    await async2()
    console.log('async1 end')
    setTimeout(() => {
      console.log('timer1')
    }, 0)
  }
  async function async2() {
    setTimeout(() => {
      console.log('timer2')
    }, 0)
    console.log('async2')
  }
  async1()
  setTimeout(() => {
    console.log('timer3')
  }, 0)
  console.log('start')


// async1 start => async2 => start => async1 end => timer2 => timer3 => timer1
首先进入async1,打印出 async1 start。

然后遇到async2,进入async2,遇到定时器 timer2,加入宏任务队列,之后打印 async2。

由于 async2阻塞了后面的代码执行,将后面的代码加入微任务队列。所以执行后面的定时器 timer3,将其加入宏任务队列,之后打印同步代码 start。

同步代码执行完毕,先执行微任务队列,打印出 async1 end,遇到定时器 timer1,将其加入宏任务队列。

最后再执行宏任务队列,宏任务队列有3个任务,先后顺序是 timer2,timer3,timer1,分别按顺序执行,任务队列按照先进先出原则执行。

四、总结

  1. async 函数 1)函数的返回值为Promise对象 2)Promise对象的结果由async函数执行的返回值决定

  2. await 表达式 1)正常情况下,await右侧的表达式一般为 promise对象 , 但也可以是其它的值 2)如果表达式是promise对象,await就忙起来了,它会阻塞函数后面的代码,等着Promise对象resolve,然后得到resolve的值,作为await表达式的运算结果。 3)如果表达式是其它值, 直接将此值作为await的返回值

  3. async和await基于promise的。使用async的函数将会始终返回一个 promise 对象。这一点很重要,要记住,可能是你遇到容易犯错的地方。

  4. 在使用await的时候我们只是暂停了函数,而非整段代码。这里经常会是容易犯错的地方。

  5. async和await是非阻塞的

  6. 仍然可以使用 Promise,例如Promise.all(p1, p2, p3).,接受一个数组作为参数,p1、p2、p3 都是 Promise 实例,如果不是,就会先调用 Promise .resolve方法,将参数转为 Promise 实例,再进一步处理。只要 p1、p2、p3 之中有一个被 rejected,整个状态就变成 rejected。

  7. 注意 1)await必须写在async函数中, 但async函数中可以没有await 2)如果await的promise失败了, 就会抛出异常, 需要通过try…catch来捕获处理

参考文档

理解异步函数async和await的用法_async await用法-CSDN博客