如何理解async/await的用法

172 阅读3分钟

promise是一个异步操作返回的消息,而async和await是基于promise的一个语法糖 语法糖!本质上和用.then没有任何区别,只是增加了可读性。

   function one(){
     return 'one'
   }
   function two(){
   return new Promise( (resolve,reject) => {
      setTimeout( () => {
       resolve('two')
    },3000)
    })
  
   }
   function three(){
    return 'three'
   }

   async function go(){
   console.log( one() )
   console.log(await two()) 
   console.log( three())
   }

   go()

image.png

打印的结果会是先打印‘one’,并在三秒后打印出two&three。

而async的用法和普通函数无差,只是把函数返回值变成一个promise对象,而await是等待,等待右边的函数或表达式执行结束后,才会执行接下来的语句,如果await等待的是一个promise对象,则在完成前会执行async外部的同步代码,完成后会接着执行await下方的代码,如果await等待的不是一个promise对象,则会直接跳出完成所有async外部同步代码,再回到async内部完成接下来的操作。

image.png

解释一下上面的话,此时将上方函数运行前后各添加一个打印,可以看到:

image.png

首先进行同步事件的处理,先打印ok1,之后进入go函数,所以接着打印one,再接着进入two中,由于await的存在,会在执行two的等待时间中接着执行async外部的同步代码,完成后才会回到await中继续往下,所以会先执行ok2的打印,此时如果将下方代码改成如下

image.png

打印的结果就变成了

image.png

由于await执行完成还未执行完ok2,所以会先回去执行three的打印才会打印OK2

接着讲讲async/await语法糖的作用: async 会将其后的函数(函数表达式或 Lambda)的返回值封装成一个 Promise 对象,而 await 会等待这个 Promise 完成,并将其 resolve 的结果返回出来。

现在举例,用 setTimeout模拟耗时的异步操作,先来看看不用 async/await 会怎么写

function takeLongTime() {
    return new Promise(resolve => {
        setTimeout(() => resolve("long_time_value"), 1000);
    });
}

takeLongTime().then(v => {
    console.log("got", v); //一秒钟后输出got long_time_value
});

如果改用 async/await 呢,会是这样

function takeLongTime() {
    return new Promise(resolve => {
        setTimeout(() => resolve("long_time_value"), 1000);
    });
}

async function test() {
    const v = await takeLongTime();
    console.log(v);  // 一秒钟后输出long_time_value
}

test();

处理then链

前面我们说了,async和await是处理then链的语法糖,现在我们来看看具体是怎么实现的:

假设一个业务,分多个步骤完成,每个步骤都是异步的,而且依赖于上一个步骤的结果。我们仍然用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');
    let time1 = 300;
    step1(time1)
        .then((time2) => step2(time2))
        .then((time3) => step3(time3))  
        .then((result) => {
            console.log(`result is ${result}`);
            console.timeEnd("doIt");
        })
}

doIt();

//执行结果为:
//step1 with 300
//step2 with 500
//step3 with 700
//result is 900
//doIt: 1510.2490234375ms

如果用 async/await 来实现呢,会是这样:

async function doIt() {
    console.time('doIt');
    let time1 = 300;
    let time2 = await step1(time1);//将Promise对象resolve(n+200)的值赋给time2
    let time3 = await step1(time2);
    let result = await step1(time3);
    console.log(`result is ${result}`);
    console.timeEnd('doIt');
}

doIt();

//执行结果为:
//step1 with 300
//step2 with 500
//step3 with 700
//result is 900
//doIt: 1512.904296875ms

显而易见,作用一样,而async/await看起来简单好用多了