如何避免 async/await 地狱

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

async/await 让咱们摆脱了回调地狱,可是这又引入了 async/await 地狱的问题。编程

图片描述

什么是 async/await 地狱数组

在 Javascript 中进行异步编程的时候,人们老是使用不少 await 语句,不少时候咱们的语句并不须要依赖于以前的语句,这样就会致使性能问题。promise

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
})()

这段代码运行没有问题。可是不是一个好的实现,由于这增长了没必要要的等待。async

说明异步编程

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

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

问题this

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

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

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

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

再来看一个更差的例子

这个 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
}
相关文章
相关标签/搜索