async/await地狱该如何避免详解


Posted in Javascript onMay 10, 2018

前言

async/await是什么

async/await可以说是co模块和生成器函数的语法糖。用更加清晰的语义解决js异步代码。

熟悉co模块的同学应该都知道,co模块是TJ大神写的一个使用生成器函数来解决异步流程的模块,可以看做是生成器函数的执行器。而async/await则是对co模块的升级,内置生成器函数的执行器,不再依赖co模块。同时,async返回的是Promise。

从上面来看,不管是co模块还是async/await,都是将Promise作为最基础的单元,对Promise不很了解的同学可以先深入了解一下Promise。

async/await 写着很爽,不过要注意这些问题。

async/await 让我们摆脱了回调地狱,但是这又引入了 async/await 地狱的问题。

async/await地狱该如何避免详解

什么是 async/await 地狱

在 Javascript 中进行异步编程的时候,人们总是使用很多 await 语句,很多时候我们的语句并不需要依赖于之前的语句,这样就会导致性能问题。

async/await 地狱的例子

我们试着写一个购买披萨和饮料的程序:

(async () => {
 const pizzaData = await getPizzaData() // async call
 const drinkData = await getDrinkData() // async call
 const chosenPizza = choosePizza() // sync call
 const chosenDrink = chooseDrink() // sync call
 await addPizzaToCart(chosenPizza) // async call
 await addDrinkToCart(chosenDrink) // async call
 orderItems() // async call
})()

这段代码运行没有问题。但是不是一个好的实现,因为这增加了不必要的等待。

说明

我们已经将我们的代码封装在异步 IIFE 中,按照下面的顺序执行:

得到披萨名单
获取饮料列表
从列表中选择一个披萨
从列表中选择一种饮料
将选中的披萨加入购物车
将选择的饮品加入购物车
订购购物车中的物品

问题

这里有个问题为什么从列表中选择披萨这个动作要等待获取饮料列表?这两个是没什么关联的操作。其中的关联操作有两组:

获取披萨列表 -》 选择披萨 -》 选择披萨加入购物车

获取饮料列表 -》 选择饮料 -》 选择饮料加入购物车

这两组操作应该是并发执行的。

再来看一个更差的例子

这个 Javascript 代码片段将购物车中的商品并发出订购请求。

async function orderItems() {
 const items = await getCartItems() // async call
 const noOfItems = items.length
 for(var i = 0; i < noOfItems; i++) {
 await sendRequest(items[i]) // async call
 }
}

这种情况 for 循环必须等待 sendRequest() 函数完成才能继续下一次迭代。但是,我们并不需要等待。我们希望尽快发送所有请求。然后我们可以等待所有请求完成。

现在你应该已经对 async/await 地狱有跟多的了解,现在我们再来考虑一个问题

如果我们忘记 await 关键字会怎么样?

如果在调用异步函数忘记使用 await,这意味着执行该功能不需要等待。异步函数将直接返回一个 promise,你可以稍后使用。

(async () => {
 const value = doSomeAsyncTask()
 console.log(value) // an unresolved promise
})()

或者是程序不清楚你想要等待函数执行完,直接退出不会完成这个异步任务。所以我们需要使用 await 这个关键字。

promise 有一个有趣的属性,你可以在某行代码中获取 promise,然后在其他地方中等待它 resolve,这是解决 async/await 地狱的关键。

(async () => {
 const promise = doSomeAsyncTask()
 const value = await promise
 console.log(value) // the actual value
})()

如你所见 doSomeAsyncTask 直接返回一个 Promise 同时这个异步函数 doSomeAsyncTask 已经开始执行,为了得到 doSomeAsyncTask 的返回值,我们需要 await 来告诉

应该如何避免 async/await 地狱

首先我们需要知道哪些命名是有前后依赖关系的。

然后将有依赖关系的系列操作进行分组合并成一个异步操作。

同时执行这些异步函数。

我们来重写这写例子:

async function selectPizza() {
 const pizzaData = await getPizzaData() // async call
 const chosenPizza = choosePizza() // sync call
 await addPizzaToCart(chosenPizza) // async call
}

async function selectDrink() {
 const drinkData = await getDrinkData() // async call
 const chosenDrink = chooseDrink() // sync call
 await addDrinkToCart(chosenDrink) // async call
}

(async () => {
 const pizzaPromise = selectPizza()
 const drinkPromise = selectDrink()
 await pizzaPromise
 await drinkPromise
 orderItems() // async call
})()

// Although I prefer it this way

(async () => {
 Promise.all([selectPizza(), selectDrink()].then(orderItems) // async call
})()

我们将语句分成两个函数。在函数内部,每个语句都依赖于前一个语句的执行。然后我们同时执行这两个函数 selectPizza()和selectDrink() 。

在第二个例子中我们需要处理未知数量的 Promise。处理这个问题非常简单,我们只需要创建一个数组将所有 Promise 存入其中,使用 Promise.all() 方法并行执行:

async function orderItems() {
 const items = await getCartItems() // async call
 const noOfItems = items.length
 const promises = []
 for(var i = 0; i < noOfItems; i++) {
 const orderPromise = sendRequest(items[i]) // async call
 promises.push(orderPromise) // sync call
 }
 await Promise.all(promises) // async call
}

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
jquery 事件执行检测代码
Dec 09 Javascript
Jquery实现弹出层分享微博插件具备动画效果
Apr 03 Javascript
js判断为空Null与字符串为空简写方法
Feb 24 Javascript
jQuery插件开发精品教程(让你的jQuery更上一个台阶)
Nov 07 Javascript
jQuey将序列化对象在前台显示地实现代码(方法总结)
Dec 13 Javascript
懒加载实现的分页&amp;&amp;网站footer自适应
Dec 21 Javascript
javascript实现用户点击数量统计
Dec 25 Javascript
BootstrapTable refresh 方法使用实例简单介绍
Feb 20 Javascript
移动前端图片压缩上传的实例
Dec 06 Javascript
vue和react等项目中更简单的实现展开收起更多等效果示例
Feb 22 Javascript
解决vue项目报错webpackJsonp is not defined问题
Mar 14 Javascript
vue-cropper插件实现图片截取上传组件封装
May 27 Vue.js
Angular4.x通过路由守卫进行路由重定向实现根据条件跳转到相应的页面(推荐)
May 10 #Javascript
JS中的JSON对象的定义和取值实现代码
May 09 #Javascript
js循环map 获取所有的key和value的实现代码(json)
May 09 #Javascript
js合并两个数组生成合并后的key:value数组
May 09 #Javascript
详解Puppeteer 入门教程
May 09 #Javascript
node基于puppeteer模拟登录抓取页面的实现
May 09 #Javascript
nuxt框架中路由鉴权之Koa和Session的用法
May 09 #Javascript
You might like
浅析php插件 Simple HTML DOM 用DOM方式处理HTML
2013/07/01 PHP
Yii框架调试心得--在页面输出执行sql语句
2014/12/25 PHP
PHP递归实现汉诺塔问题的方法示例
2017/11/25 PHP
PHP生成加减算法方式的验证码实例
2018/03/12 PHP
基于jQuery的简单的列表导航菜单
2011/03/02 Javascript
javascript 学习笔记(八)javascript对象
2011/04/12 Javascript
js判断文本框剩余可输入字数的方法
2015/02/04 Javascript
jQuery实现自定义右键菜单的树状菜单效果
2015/09/02 Javascript
浅析node连接数据库(express+mysql)
2015/11/30 Javascript
如何消除inline-block属性带来的标签间间隙
2016/03/31 Javascript
Bootstrap如何创建表单
2016/10/21 Javascript
AngularJS自定义指令之复制指令实现方法
2017/05/18 Javascript
使用angular帮你实现拖拽的示例
2017/07/05 Javascript
JavaScript函数节流和函数去抖知识点学习
2018/07/31 Javascript
Vue项目全局配置页面缓存之按需读取缓存的实现详解
2018/08/01 Javascript
微信小程序对图片进行canvas压缩的方法示例详解
2020/11/12 Javascript
[01:00:06]加油DOTA_EP01_网络版
2014/08/09 DOTA
[02:32]“虐狗”镜头慎点 2016国际邀请赛中国区预选赛现场玩家采访
2016/06/28 DOTA
Python入门_浅谈for循环、while循环
2017/05/16 Python
python学习笔记之列表(list)与元组(tuple)详解
2017/11/23 Python
python 3.6 +pyMysql 操作mysql数据库(实例讲解)
2017/12/20 Python
Python绘制3D图形
2018/05/03 Python
Python利用lxml模块爬取豆瓣读书排行榜的方法与分析
2019/04/15 Python
python常用函数与用法示例
2019/07/02 Python
python 实现视频 图像帧提取
2019/12/10 Python
新手学python应该下哪个版本
2020/06/11 Python
css3模拟jq点击事件的实例代码
2017/07/06 HTML / CSS
简单几步用纯CSS3实现3D翻转效果
2019/01/17 HTML / CSS
预订从美国飞往印度的机票:MyTicketsToIndia
2017/05/19 全球购物
第一范式(1NF)、第二范式(2NF)和第三范式(3NF)之间的区别是什么?
2016/04/28 面试题
DTD的含义以及作用
2014/01/26 面试题
采购部部门职责
2013/12/15 职场文书
毕业生就业推荐表自我鉴定
2014/03/20 职场文书
秋冬农业生产标语
2014/10/09 职场文书
旷课检讨书500字
2014/10/14 职场文书
详解Javascript实践中的命令模式
2021/05/05 Javascript