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