目录
背景
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) //统一处理异常 }}
- 如果略过async和await,上面的代码风格看起来和同步代码一样。
- async声明的函数,一定会返回一个promise,也就是说这个函数返回后,可以调用.then进行链式调用,如下代码代码所示:
fetchGroupAndActivities().then(() => { console.log("address groups and activities.") })
- 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
}
注意事项
- 关键字await必须在async异步函数内使用。
- async函数的返回值,是一个promise对象。如果用户没有返回或者返回普通对象,js环境打包时会自动为你包装一个promise的对象。
代码执行顺序
- async函数执行时,在遇到await关键字之前,它与调用它的函数,都是同步运行的。
- 当遇到await关键字,async函数将被挂起,且释放cpu控制权,允许其他函数继续执行, 直到await声明的异步函数返回结果。一旦完成,程序将从await语句下一行代码开始执行。
缺陷
- 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可能带来的问题。