当前位置: 代码迷 >> 综合 >> JS核心-异步编程中的async/await
  详细解决方案

JS核心-异步编程中的async/await

热度:89   发布时间:2023-12-21 03:28:47.0

目录

背景

Await/async与Promise比较

Await/async搭配Promise.all()使用

注意事项

代码执行顺序

缺陷


背景

上文提到的Promise用来处理异步操作结果,配合then注册回调函数, 形成链式调用,让代码易读。除此之外, JS还提供了一个语法糖async/await对Promise使用进行封装,可使异步代码看起来像同步代码的样式,使得代码容易阅读和维护。本文介绍Async/await的使用和注意事项。

Await/async与Promise比较

   /*** 加载活动列表(其中先加载群组以得到活动的头像)*/fetchGroupAndActivities: function(){if(this.data.isLogin) {var that = thisgetGroups() //先加载群组列表的头像。.then((res)=>{if(res.data.code == "10000") { ...return getActivities()  //其次,加载活动列表}}).then((res)=>{ //链式调用,处理活动列表数据。if (res.data.code == "10000") {...}}).catch((err) => { //统一捕获异常。 上面then中的任意回调发送异常,会直接中断调用链,在这里处理。console.log("get act and group failed...")})

使用Await/async重构后的代码形式

 /*** 加载活动列表(其中先加载群组以得到活动的头像)*/fetchGroupAndActivities: async function(){if(this.data.isLogin) {try {var res = await getGroups() //1. 异步加载群组列表。if(res.data.code == "10000") { ...var activitiesRes= await getActivities()  //2.异步加载活动列表if (activitiesRes.data.code == "10000") {...//处理活动列表数据}}} catch(e) {  console.log("get act and group failed...e = " + e) //统一处理异常 }}
  1. 如果略过async和await,上面的代码风格看起来和同步代码一样。
  2. async声明的函数,一定会返回一个promise,也就是说这个函数返回后,可以调用.then进行链式调用,如下代码代码所示:
    fetchGroupAndActivities().then(() => { console.log("address groups and activities.") })
  3. await关键字用来声明一个返回promise的异步操作。它可以挂起函数,允许其他函数代码得到执行,直到异步操作返回结果(promise为fulfiled状态)。async函数继续从await语句下一行开始执行。

Await/async搭配Promise.all()使用

需求:获取群组列表和活动列表

分析:两组数据源,全部的结果返回后,再依次消费这些结果。

代码:

async function fetchIndexList() {var results = await Promise.all([getGroups, getActivities])return results //这里被js自动封装成Promise
}

可以看到如果用Promise.all()API,代码行数少。如果不用Promise,改怎么写?

async function fetchIndexList() {var groups = getGroups();var activities = getActivities();var result1 = await groupsvar result2 = await activitiesreturn [result1, result2]//这里被js自动封装成Promise
}

注意事项

  1. 关键字await必须在async异步函数内使用。 
  2. async函数的返回值,是一个promise对象。如果用户没有返回或者返回普通对象,js环境打包时会自动为你包装一个promise的对象。

代码执行顺序

  • async函数执行时,在遇到await关键字之前,它与调用它的函数,都是同步运行的。
  • 当遇到await关键字,async函数将被挂起,且释放cpu控制权,允许其他函数继续执行, 直到await声明的异步函数返回结果。一旦完成,程序将从await语句下一行代码开始执行。

缺陷

  1. await不正确使用带来的阻塞问题

通过一个计时器代码来测量await可能带来的速度问题。

1. 定义计时器A

function A(){return new Promise((resovle, reject) => {setTimeout({resolve()}, 3000)})
}

2. 定义计时器B

function B(){return new Promise((resovle, reject) => {setTimeout({resolve()}, 3000)})
}

3. 启动两个计数器,统计总共消耗多少时间

async function startTimer(){var startTime = new Date()var resultA = await A()var resultB = await B()setTimeout({var finishTime = new Date()var totalTime = finishTime - startTimeconsole.log("totalTime = " + totalTime)},3000)
}startTimer()

   上述代码执行后,计时器总共耗时大于9s。 await标记的异步任务会一个等待前一个await标记的任务执行完毕,如果await依赖过多,产生的执行时间可能更长。有时候你只是想让promise同时执行,有没有一种办法减少时间消耗?解决办法:

async function startTimer(){var startTime = new Date()var resultA = A()var resultB = B()await resultAawait resultBsetTimeout({var finishTime = new Date()var totalTime = finishTime - startTimeconsole.log("totalTime = " + totalTime)},3000)
}startTimer()

把代表计时器的Promise保存在变量里面,然后再去等待他们的结果。

这样看起来三个计时器Promise是同时启动,它们也将同时完成。最终输出的totalTime大约只有3s。

2. await必须和async搭配使用,违背的代码设计原则, async和await耦合起来了,开发者容易忘记声明async,造成错误。

 总结

本文介绍了JS异步编程中的语法糖await/async来包装Promise,使得异步代码看起来像同步代码的风格,让代码变得简洁。同时介绍了async函数执行的流程,以及使用await可能带来的问题。

  相关解决方案