首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

避免陷入 async/await 地狱

async/await 是 ES7 的新语法。在 async/await 标准出来之前,JavaScript 的异步编程经历了 callback --> promise --> generator 的演变过程。在 callback 的时代,最让人头疼的问题就是回调地狱(callback hell)。所以,在 async/await 一经推出,社区就有人认为「这是 JavaScript 异步编程的终极解决方案」。

但用了 async/await 就真的没有问题了吗?

最近阅读了 Aditya Agarwal 的一篇文章:How to escape async/await hell。这篇文章主要讨论了过度使用 async/await 导致的新的「地狱」问题,其已经在 Medium 上获得了 19k+ 的 Applause。

好不容易逃离了一个「地狱」,又马上陷入另一个「地狱」了。

何为 async/await 地狱

在编写异步代码时,人们总是喜欢一次写多个语句,并且在一个函数调用之前使用 关键字。这可能会导致性能问题,因为很多时候一个语句并不依赖于前一个语句——但使用 关键字后,你就需要等待前一个语句完成。

示例

假设你要写一个订购 pizza 和 drink 的脚本,代码可能是如下这样的:

这段代码开起来没什么问题,也能正常的运行。但是,这并不是一个好的实现,因为这把本身可以并行执行的任务变成了串行执行。

选择一个 drink 添加到购物车和选择一个 pizza 添加到购物车可以看作是两个任务,而这两个任务之间并没有相互依赖的关系,也没有特定的顺序执行关系。所以这两个任务是可以并行执行的,这样能提高性能。而上述代码将二者变成了串行执行,显然是降低了程序性能的。

更糟糕的例子

假设要写一个程序,根据 followers 数用来显示 Github 中国区用户的排名情况。

如果只是获取排名,我们可以调用 Github 官方的 Search users 接口,伪代码如下:

调用 函数就能获取到想要的结果。但是,你可能还要想要获取每个用户的 follower 数、email、地区和仓库等数据,而 Search users 接口并没有返回这些数据,你可能需要再去调用 Single user 接口。

然后上述代码可能被改写为:

运行查看结果,自己想要的数据都拿到了。但是,你发现一个问题,程序运行时间有点长,该怎么优化下呢?

其实,铺垫了这么长,就是想说明一个问题:你陷入了 async/await 的地狱

上述代码的问题在于,获取每个用户资料的请求并不存在依赖性,就类似上文中的选择 pizza 和 drink 一样,这是可以并行执行的请求。而根据上述代码,请求都变成了串行执行,这当然会损耗程序的性能。

按照上述代码,可以看一下其异步请求的动态图:

images

可以看到,获取用户资料的每个请求都需要等到上一个请求完成之后才能执行,Waterfall 处于一个串行的状态。那要怎么改进这个问题呢?

既然获取每个用户资料的请求并不存在依赖性,那么我们可以先触发异步请求,然后延迟处理异步请求的结果,而不是一直等该请求完成。根据这个思路,那可能改进的代码如下:

可以看一下异步请求的动态图:

images

可以看到,获取用户资料的异步请求处理不再是串行执行,而是并行执行了,这将大大提高程序的运行效率和性能。

总结

Aditya Agarwal 在其文章中也给出了怎么避免陷入 async/await 地狱的建议:

首先找出依赖于其他语句的执行的语句

然后将有依赖关系的一系列语句进行组合,合并成一个异步函数

最后用正确的方式执行这些函数

参考

How to escape async/await hell

精读《async/await 是把双刃剑》

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180520G02W9V00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券