【JS进阶】async/await

文章目录

  • 一.认识async/await
    • 1.async
    • 2.await
  • 二.async/await如何捕获异常
  • 三.async/await优势
  • 四.async/await对比Promise的优势


一.认识async/await

async/await其实是Generator的语法糖,它能实现的效果都能用then链来实现,它是为优化then链而开发出来的。从字面上来看,async是“异步”的简写,await是等待,所以很好理解async是用于声明异步的,而await用于等待一个异步方法执行完成。当然语法上强制规定await只能出现在asnyc函数中。

1.async

  • async函数返回的是一个Promise对象。async函数乳瓜在函数中return一个直接量,async会把这个直接量通过Promise.resolve() (Promise.resolve()可以看做是new Promise(resolve => resolve()的简写)封装成Promise对象。
  async function test(){
    return 'hello world'
  }

  let res = test()
  console.log(res)

【JS进阶】async/await_第1张图片

  • async函数返回的是一个Promise对象,所以在最外层不能用await获取其返回值的情况下,用原来的方式:then()来处理这个Promise对象:
  async function test(){
    return 'hello world'
  }

  let res = test()
  console.log(res) //Promise对象
  res.then(result => {
    console.log(result) //hello world
  })
  • 联想一下Promise的特点——无等待,所以在没有await的情况下执行async函数,它会立即执行,返回一个Promise对象,并且,绝不会阻塞后面的语句。
  async function test(){
    return 'hello world'
  }

  let res = test()
  console.log(res)
  res.then(result => {
    console.log(result)
  })
  console.log("aaa") //立即执行aaa

2.await

一般来说,都认为await是在等待一个async函数完成。不过按语法说明,await等待的是一个表达式,这个表达式的计算结果是Promise对象或者其他值。因为async函数返回一个Promise对象,所以await可以用于等待一个async函数的返回值——也可以说是await是在等async函数,但要清楚,它等的实际是一个返回值。注意到await不仅仅用于等Promise对象,它可以等任意表达式的结果,所以await后面实际是可以接普通函数调用或者直接量的。

  • await表达式的结果取决于它等的是什么:
    • 如果等到的不是一个Promise对象,那么await表达式的运算结果就是它等到的东西
    • 如果等到的是一个Promise对象,那么await就忙起来了,它会阻塞后面的代码,等着Promise对象resolve,然后得到resolve的值,作为await表达式的运算结果。
  function test1(){
    return "test1"
  }

  async function test2(){
    return "test2"
    // return Promise.resolve("test2")
  }

  async function test3(){
    const res1 = await test1() //不是Promise对象
    const res2 = await test2() //Promise对象
    console.log(res1,res2) //test1 test2 
  }

  test3()
  • await必须用在async函数中。async函数调用不会造成阻塞,它内部所有的阻塞都被封装在一个Promise对象中异步执行。await会暂停当前async的执行。
 function test1(x){
    return new Promise(resolve => {
      setTimeout(() =>{
        resolve(x)
      },3000)
    })
  }

  async function test2(){
    let res = await test1('hello world')
    console.log(res)   //3s后输出hello world
    console.log("aaa") //3s后输出aaa
  }

  test2()
  console.log("bbb") //立即输出bbb

二.async/await如何捕获异常

  • 如果await后面的异步操作出错,那么等同于async函数返回的Promise对象被reject。
  async function test(){
    return Promise.reject('错误')
  }

  test().then(res => {
    console.log("success:",res)
  },err => {
    console.log("error:",err)
  })
 //error:错误
  • 防止出错的方法,是将其放在try…catch代码块中。
    async function test(){
    try{
      await new Promise(function(resolve,reject){
        throw new Error('错误')
      });
    } catch(err){
      console.log("error:",err)
    }
    return await('成功');
  }

三.async/await优势

单一的 Promise 链并不能发现 async/await 的优势,但是,如果需要处理由多个 Promise 组成的 then 链的时候,优势就能体现出来了(很有意思,Promise 通过 then 链来解决多层回调的问题,现在又用 async/await 来进一步优化它)。
假设一个业务,分多个步骤完成,每个步骤都是异步的,而且依赖于上一个步骤的结果。仍然用 setTimeout 来模拟异步操作:

/**
 * 传入参数 n,表示这个函数执行的时间(毫秒)
 * 执行的结果是 n + 200,这个值将用于下一步骤
 */
function takeLongTime(n) {
    return new Promise(resolve => {
        setTimeout(() => resolve(n + 200), n);
    });
}
function step1(n) {
    console.log(`step1 with ${n}`);
    return takeLongTime(n);
}
function step2(n) {
    console.log(`step2 with ${n}`);
    return takeLongTime(n);
}
function step3(n) {
    console.log(`step3 with ${n}`);
    return takeLongTime(n);
}

现在用 Promise 方式来实现这三个步骤的处理:

function doIt() {
    console.time("doIt");
    const time1 = 300;
    step1(time1)
        .then(time2 => step2(time2))
        .then(time3 => step3(time3))
        .then(result => {
            console.log(`result is ${result}`);
            console.timeEnd("doIt");
        });
}
doIt();
// c:\var\test>node --harmony_async_await .
// step1 with 300
// step2 with 500
// step3 with 700
// result is 900
// doIt: 1507.251ms

输出结果 result 是 step3() 的参数 700 + 200 = 900。doIt() 顺序执行了三个步骤,一共用了 300 + 500 + 700 = 1500 毫秒,和 console.time()/console.timeEnd() 计算的结果一致。
如果用 async/await 来实现呢,会是这样:

async function doIt() {
    console.time("doIt");
    const time1 = 300;
    const time2 = await step1(time1);
    const time3 = await step2(time2);
    const result = await step3(time3);
    console.log(`result is ${result}`);
    console.timeEnd("doIt");
}
doIt();

结果和之前的 Promise 实现是一样的,但是这个代码看起来是不是清晰得多,几乎跟同步代码一样

四.async/await对比Promise的优势

  • 代码读起来更加同步,Promise虽然摆脱了回调地狱,但是then的链式调用也会带来额外的阅读负担。
  • Promise传递中间值非常麻烦,而async/await几乎是同步的写法,非常优雅。
  • 错误处理友好,async/await可以用成熟的try/catch,Promise的错误捕获非常冗余。。
  • 调试友好,Promise的调试很差,由于没有代码块,你不能在一个返回表达式的箭头函数中设置断点,如果你在一个.then代码块中使用调试器的步进功能,调试器并不会进入后续的.then代码块,因为调试器只能跟踪同步代码的每一步。

你可能感兴趣的:(JavaScript,javascript,前端,开发语言)