初探async/await
一用就能感觉到,async和await是目前处理异步逻辑最优雅的方式。
async/await 特点概要
- 属于ES2017,应用在项目中需要babel(具体是stage-3),node最新版已经支持
- 就是
Generator
函数的语法糖 - 内置执行器,与普通函数一模一样,只要一行便可执行
async
函数async
表示函数里有异步操作async
函数完全可以看作多个异步操作,包装成的一个Promise
对象async
函数返回的是Promise
对象,可以作为await
命令的参数async
函数内部return
语句返回的值,会成为then
方法回调函数的参数- 只有
async
函数内部的异步操作执行完,才会执行then
方法指定的回调函数
await
命令await
表示紧跟在后面的表达式需要等待结果await
命令就是内部then
命令的语法糖- 只要一个
await
语句后面的Promise
变为reject
,那么整个async
函数都会中断执行。- 不希望如此则用
try...catch...
语句同步形式处理 - 或者在await后的promise对象跟上
catch
方法
- 不希望如此则用
async/await 使用姿势
- 函数声明
async function foo() {}
- 函数表达式
const foo = async function () {};
const foo = async () => {};
- 对象的方法
let obj = { async foo() {} }; obj.foo().then(...);
- class的方法12345678910111213141516class User {constructor() {super()}getUserInfo(url) {return fetch(url).then(res => res.user)}async foo() {const user_info = await this.getUserInfo(url)dosomething...}}const user = new User()user.foo().then(...)
async/await 基本实践
async/await最基本的用法,就是在async函数执行的时候,遇到await命令,就会等到await后的promise执行完毕,再接着执行函数体await之后的语句
这样,就会让人觉得这其实是一段同步的代码,按顺序自上而下执行。真正异步的东西其实在await部分,以及函数返回promise之后。
下面记下自己的亲自实践过程,来更好的解释async和await的用法。
业务场景是,后台给我们一组实例id,我们需要通过遍历这些id来异步向后台请求,获取任务task。(有人估计问为什么不后台直接返回过来任务task。。据后台同事说,这种前端通过遍历发送多个异步请求的效率比较高。。)
起初,为了赶工期。。是这么写的
上面这段代码,有两处调用了setState,还是为了在异步请求之后再设置state,要是写在循环外面,就会先于异步请求执行。显得很冗余,也很蠢。。
最重要的是,循环的setState会使React组件render很多次。要是嵌套了子组件,子组件中有异步请求代码的话,也会使子组件发送很多重复的请求。
所以,这段代码既丑陋又影响性能。为了解决这个问题,尝试用async和await重构这段逻辑
上面这段代码看起来就清晰多了,由于async函数返回的promise对象,所以可以将获取task和设置task分开来,使一个方法做一个任务,方便维护。
(上面的代码也是修改了很多次,经过了很多尝试,第一版:用函数表达式的写法写,在方法里调用。第二版:改成class的async方法,仍然在方法里setState)
但是,上面的代码还是存在一个问题,不要忘了,紧跟在await
后面的表达式,需要等待结果,然后才能继续执行。所以上面for循环发的一个个requireTask请求是等上一个requireTask请求完毕后,再发起下一个请求的,也就是说,这里一系列requireTask请求是继发的。然而,每个requireTask请求是完全独立的,如果是继发的话,会比较耗时,影响性能。
所以,有了下面的版本,将继发的请求变成并发的形式:
后记
上面的代码应该还有值得优化的地方,主要参考了阮老师的ECMAScript 6 入门,其中提到的async函数的难点在于错误处理,这里还需要考虑错误的处理。在本文中就不赘述了。
Author: Wang He
Origin: http://wanghewanghe.github.io
Link: http://wanghewanghe.github.io/2017/03/29/初探async/await/
本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可