前端开发:Async/Await的使用心得

接着三掌柜的上一篇关于Promise的使用的博文来继续讲,前端处理异步回调操作用到的方法之一:Async/Await。可以说Async/Await是Promise的升级版,而且应用了Async/Await的异步请求的情况下,可以让代码看起来像同步方式调用解决异步回调。

在ES7之前,了解到Promise是ES6为了解决异步回调而产生的解决方案,避免出现回调地狱(Callback Hell),那么ES7为什么又提出了新的Async/Await标准?问题答案就是:Promise虽然解决了异步嵌套的怪圈,使用表达清晰的链式表达;但是如果在实际开发过程中有些地方有大量的异步请求的时候,而且流程复杂嵌套的情况下,检查相关代码会发现一个比较尴尬。

一、Async/Await是什么?

Async/Await是基于Promise而来的,Async/Await是相互依存的,缺一不可,它们的出现就是为了Promise而来,也算是Promise的进化改良版,为的就是解决文章开始说的如果出现大量复杂嵌套不易读的Promise异步问题。

1、Async/Await基本含义

①Async/Await是基于Promise实现的,是写异步代码的新方式,它们不能用于普通的回调函数;

②Async/Await也是非阻塞的;

③Async/Await的写法使得异步代码看起来像同步代码,简洁、便于读取。

2、Async/Await的语法

async必须声明的是一个function函数,await就必须是在async声明的函数内部使用,这是一个固定搭配,任何一种不符合这两个条件,程序就会报错,具体举实例来直观看一下:

let data = 'data'

a = async function () {

    const b = function () {

        await data

    }

}

二、Async/Awaitd的本质

1、 Async的本质

async是对generator的再一次语法糖封装,帮助实现了生成器的调用,使语句更贴近同步代码的表达方式,可以将async函数看做是多个异步操作封装的 promise对象。

async声明的函数的返回其实就是一个Promise,也就是说只要声明的函数是async,不管内部怎么处理,它返回的一定是一个Promise,举个例子如下所示:

(async function () {

    return 'Promis+++‘

})() // 返回的是Promise+++

2、 Awaitd的本质

await的本质其实是可以提供等同于“同步效果”的等待异步返回能力的语法糖,也就是then的语法糖。如果想使用await来执行一个异步操作,那么其调用函数必须使用async来声明。

await能返回一个Promise对象,也能返回一个值。若await返回的是Promise对象,那么还可以继续给await的返回值使用then函数。举个实例看一下:

const a = async ()=>{

    let message = '声明值111’

    let result = await message;

    console.log('由于上面的程序还没执行完,“等待一会”');

    return result

}

a().then(result=>{

  console.log('输出',result);

})

三、 Async/Await的优势

为什么说Async/Awaitd比Promise更胜一筹?具体原因如下所示。

1、 简洁明了

根据上述关于Async/Awaitd的实例可以看到,Async/Awaitd的写法很简单,相比Promise的写法,不用写.then,不用写匿名函数处理Promise的resolve值,也不用定义多余的data变量,更避免了嵌套代码的操作,大大省去了很多代码行,使得处理异步操作的代码简洁明了,方便查阅和精准定位。

2、 错误处理的方式

Async/Await可以让try/catch同时处理同步和异步的错误,而且在Promise中try/catch不能处理JSON.parse的错误,在Promise中需要使用.catch,但是错误处理的那坨代码会非常冗余,要知道实际开发过程中代码会比理论上的情况会更复杂。

通过使用Async/Await,try/catch能处理JSON.parse的错误,具体实例如下所示:
   

 const request = async () => {

        try {

            const data = JSON.parse(await getJSON())

            console.log(data)

        } catch (err) {

            console.log(err)

        }

 }

3、 条件语句

通过使用Async/Await,可以使得条件语句写法简洁又可以提高代码可读性,这里就不再举对比的例子,只举一个Async/Await的使用实例来说:

const request = async () => {

  const data = await getJSON()

  if (data.anotherRequest) {

    const moreData = await anotherRequest(data);

    console.log(moreData)

    return moreData

  } else {

    console.log(data)

    return data    

  }

}

4、 中间值

在实际开发过程中会遇到这种场景:调用promise1,使用promise1返回的结果再去调用promise2,然后使用两者的结果去调用promise3。在没有使用Async/Await之前的写法,应该是这样的:

const request = () => {

     eturn promise1()

          .then(value1 => {

         return promise2(value1)

        .then(value2 => {        

               return promise3(value1, value2)

        })

    })

}

使用了Async/Await的写法之后,是这样的:

前端开发:Async/Await的使用心得

const request = async () => {

  const value1 = await promise1()

  const value2 = await promise2(value1)

  return promise3(value1, value2)

}

通过上述两个写法,直观的看出来使用Async/Await之后会使得代码变得非常整洁简单,直观,高可读性。

5、 错误栈对比

如果实例中调用多个Promise,其中的一个Promise出现错误,Promise链中返回的错误栈没有显示具体的错误发生的位置信息,这就造成排查错误的耗时时长和解决的难度,甚至会起到反作用,假设错误栈的唯一函数名为errorPromise,但是它和错误没有直接关系。如果使用了Async/Await之后,错误栈会直接指向错误所在的函数,这样更清晰直观的方便排查问题所在,尤其是在查看分析错误日志的时候非常有效有用。

6、 调试

通过上面描述的Async/Await优点中,一直在反复强调Async/Await会使得代码简洁明了,其实在调试过程中,Async/Await也可以使得代码调试起来很轻松简单,相对于Promise来讲,不用再写太多箭头函数,可以直接像调试同步代码一样单步走,跳过await语句。

7、 中断/终止程序

首先要明确知道,Promise自身是不能终止的,Promise本身只是一个状态机,存储了三种状态,一旦进行发出请求,就必须要闭环,无法进行取消操作,就算是在前面讲到的pending状态,也只是一个挂起请求的状态,但不是取消。

但是使用Async/Await的时候,想要终止程序就很简单,那是因为Async/Await语义化很明显,和一般的function的写法类似,想要终端程序的时候,直接return一个值(“”、null、false等)就可以了,实际上就是直接返回一个Promise。具体实例如下所示:

let count = 3;

const a = async ()=>{

  const result = await delay(2000);

  const result1 = await delaySecond(count);

  if (count > 2) {

      return '';

    // return false;

    // return null;

  }

  console.log(await delay(2000));

  console.log(‘结束’);

};

a().then(result=>{

  console.log(result);

})

.catch(err=>{

  console.log(err);

})

async函数本质就是返回一个Promise。

四、实际开发过程中异步操作需要注意的事项

Async/Await使用for循环获取数据(串行)

根据上面Promise的for循环获取数据来做对比,直接使用上述实例的场景,来看看Async/Await的写法,具体操作如下所示:

(async ()=>{

    array = [timeout(2000), timeout(1000), timeout(1000)]

    for (var i=0; i < array.length; i++) {

        result = await array[i]();

        console.log(result);

    }

})()

通过对比,在这里还要夸一下Async/Await,直观的可以看到同样的需求,使用Async/Await来实现是不是非常的方便和简洁。

 

以上就是本章全部内容,欢迎关注三掌柜的微信公众号“程序猿by三掌柜”,三掌柜的新浪微博“三掌柜666”,欢迎关注!

 

https://www.361zimeiti.cn/402665.html
https://www.361zimeiti.cn/402675.html
上一篇:Python使用signal定时结束AsyncIOScheduler任务


下一篇:await async axios