简评: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

}

如何避免 async/await 地狱的更多相关文章

  1. js中promise解决callback回调地狱以及使用async+await异步处理的方法

    1.callback回调地狱 function ajax(fn) { setTimeout(()=> { console.log('你好') fn() }, 1000) } ajax(() =& ...

  2. Promise,async/await解决回调地狱

    先说一下async的用法,它作为一个关键字放到函数前面,用于表示函数是一个异步函数,因为async就是异步的意思, 异步函数也就意味着该函数的执行不会阻塞后面代码的执行. 写一个async 函数 as ...

  3. [微信小程序] 终于可以愉快的使用 async/await 啦

    [小程序] 终于可以愉快的使用 async/await 啦 这篇文章主要是想说一下 怎么在微信小程序中使用async/await从而逃离回调地狱 背景 最近一直在搞微信小程序 用的语言是TypeScr ...

  4. Javascript中的async await

    async / await是Javascript是ES7的重要特性之一,也是目前社区里公认的优秀异步解决方案.目前,async / await这个特性已经是stage 3的建议,可以看看TC39的进度 ...

  5. async/await与promise(nodejs中的异步操作问题)

    此文只是粗略介绍使用方法,欲了解核心概念请参考官方文档或其他资料. 举例写文章详情页面的时候的一个场景:首先更改文章详情中的 PV,然后读取文章详情,然后根据文章详情中文章 Id 查阅该文章评论和该文 ...

  6. Async/Await替代Promise的6个理由

    译者按: Node.js的异步编程方式有效提高了应用性能:然而回调地狱却让人望而生畏,Promise让我们告别回调函数,写出更优雅的异步代码:在实践过程中,却发现Promise并不完美:技术进步是无止 ...

  7. async/await,了解一下?

    上一篇博客我们在现实使用和面试角度讲解了Promise(原文可参考<面向面试题和实际使用谈promise>),但是Promise 的方式虽然解决了 callback hell,但是这种方式 ...

  8. promise async await使用

    1.Promise (名字含义:promise为承诺,表示其他手段无法改变) Promise 对象代表一个异步操作,其不受外界影响,有三种状态: Pending(进行中.未完成的) Resolved( ...

  9. Async/Await 学习与示例

    参考:Async/await学习 es 7 提供了对 promise 对象的更好的操作,省去了很多丧心病狂的链式异步请求,promise 是回调地狱的福音,而 Async/Await 则是 promi ...

随机推荐

  1. python构造一个freebuf新闻发送脚本

    前言: 放假学习完web漏洞后.想写一个脚本 然而自己菜无法像大佬们一样写出牛逼的东西 尝试写了,都以失败告终. 还有一个原因:上学时间不能及时看到,自己也比较懒.邮件能提醒自己. 需要安装的模块: ...

  2. Spring邮件发送2

    前言:上一篇博文讲解了邮件发送的基础用法(数据是写死的),然而在实际开发中,大多数情况下邮件内容都是根据业务来动态生成的.所以在此篇博文中,我们将讲解邮件发送携带数据的几种方案. 一.解析自定义占位符 ...

  3. 作业三:模拟 mysql 进行增删改查

    # !/usr/bin/env python3 # _*_coding:utf-8_*_ def help_sql(cmd): if cmd in func_dic.keys(): print('{} ...

  4. VMware网络配置

    NAT模式 首先保证虚拟机网卡和主机对接,虚拟机网络连接要和主机在同一网段 1. 控制面板\网络和 Internet\网络连接中配置VMnet8 2. 编辑虚拟机网络配置 此处子网ip需要和Vnet8 ...

  5. gradle入门(1-4)多项目构建实战

    一.多项目构建 1.多项目构建概念 尽管我们可以仅使用单个组件来创建可工作的应用程序,但有时候更广泛的做法是将应用程序划分为多个更小的模块. 因为这是一个非常普遍的需求,因此每个成熟的构建工具都必须支 ...

  6. React-redux使用中有关Provider问题

    先上错误: Warning: Failed prop type: Invalid prop `children` of type `array` supplied to `Provider`, exp ...

  7. Django admin 中抛出 'WSGIRequest' object has no attribute 'user'的错误

    这是Django版本的问题,1.9之前,中间件的key为MIDDLEWARE_CLASSES, 1.9之后,为MIDDLEWARE.所以在开发环境和其他环境的版本不一致时,要特别小心,会有坑. 将se ...

  8. Extensions in UWP Community Toolkit - ViewExtensions

    概述 UWP Community Toolkit Extensions 中有一个为 View 提供的扩展 - View Extensions,本篇我们结合代码详细讲解 View Extensions  ...

  9. ipv4与ipv6的区别

    对于计算机网络有一定了解的园园们来说,对这两个概念应该比较熟悉,我也将我知道的一点点小知识分享给大家吧 1.协议使用的广泛程度不同 目前,ipv4得到了广泛的应用,基本上所以的与上网有关的(看电影,玩 ...

  10. line-height与图片底部间隙的学习整理转述

    前言:这是笔者学习之后自己的理解与整理.如果有错误或者疑问的地方,请大家指正,我会持续更新! 看大牛张鑫旭的视屏可能会理解的更深一些,点击这里: line-height,两行文字的基线之间的距离: 基 ...