async/await地獄該如何避免詳解
前言
async/await是什么
async/await可以說是co模塊和生成器函數(shù)的語法糖。用更加清晰的語義解決js異步代碼。
熟悉co模塊的同學(xué)應(yīng)該都知道,co模塊是TJ大神寫的一個使用生成器函數(shù)來解決異步流程的模塊,可以看做是生成器函數(shù)的執(zhí)行器。而async/await則是對co模塊的升級,內(nèi)置生成器函數(shù)的執(zhí)行器,不再依賴co模塊。同時(shí),async返回的是Promise。
從上面來看,不管是co模塊還是async/await,都是將Promise作為最基礎(chǔ)的單元,對Promise不很了解的同學(xué)可以先深入了解一下Promise。
async/await 寫著很爽,不過要注意這些問題。
async/await 讓我們擺脫了回調(diào)地獄,但是這又引入了 async/await 地獄的問題。
什么是 async/await 地獄
在 Javascript 中進(jìn)行異步編程的時(shí)候,人們總是使用很多 await 語句,很多時(shí)候我們的語句并不需要依賴于之前的語句,這樣就會導(dǎo)致性能問題。
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 })()
這段代碼運(yùn)行沒有問題。但是不是一個好的實(shí)現(xiàn),因?yàn)檫@增加了不必要的等待。
說明
我們已經(jīng)將我們的代碼封裝在異步 IIFE 中,按照下面的順序執(zhí)行:
得到披薩名單
獲取飲料列表
從列表中選擇一個披薩
從列表中選擇一種飲料
將選中的披薩加入購物車
將選擇的飲品加入購物車
訂購購物車中的物品
問題
這里有個問題為什么從列表中選擇披薩這個動作要等待獲取飲料列表?這兩個是沒什么關(guān)聯(lián)的操作。其中的關(guān)聯(lián)操作有兩組:
獲取披薩列表 -》 選擇披薩 -》 選擇披薩加入購物車
獲取飲料列表 -》 選擇飲料 -》 選擇飲料加入購物車
這兩組操作應(yīng)該是并發(fā)執(zhí)行的。
再來看一個更差的例子
這個 Javascript 代碼片段將購物車中的商品并發(fā)出訂購請求。
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 循環(huán)必須等待 sendRequest() 函數(shù)完成才能繼續(xù)下一次迭代。但是,我們并不需要等待。我們希望盡快發(fā)送所有請求。然后我們可以等待所有請求完成。
現(xiàn)在你應(yīng)該已經(jīng)對 async/await 地獄有跟多的了解,現(xiàn)在我們再來考慮一個問題
如果我們忘記 await 關(guān)鍵字會怎么樣?
如果在調(diào)用異步函數(shù)忘記使用 await,這意味著執(zhí)行該功能不需要等待。異步函數(shù)將直接返回一個 promise,你可以稍后使用。
(async () => { const value = doSomeAsyncTask() console.log(value) // an unresolved promise })()
或者是程序不清楚你想要等待函數(shù)執(zhí)行完,直接退出不會完成這個異步任務(wù)。所以我們需要使用 await 這個關(guān)鍵字。
promise 有一個有趣的屬性,你可以在某行代碼中獲取 promise,然后在其他地方中等待它 resolve,這是解決 async/await 地獄的關(guān)鍵。
(async () => { const promise = doSomeAsyncTask() const value = await promise console.log(value) // the actual value })()
如你所見 doSomeAsyncTask 直接返回一個 Promise 同時(shí)這個異步函數(shù) doSomeAsyncTask 已經(jīng)開始執(zhí)行,為了得到 doSomeAsyncTask 的返回值,我們需要 await 來告訴
應(yīng)該如何避免 async/await 地獄
首先我們需要知道哪些命名是有前后依賴關(guān)系的。
然后將有依賴關(guān)系的系列操作進(jìn)行分組合并成一個異步操作。
同時(shí)執(zhí)行這些異步函數(shù)。
我們來重寫這寫例子:
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 })()
我們將語句分成兩個函數(shù)。在函數(shù)內(nèi)部,每個語句都依賴于前一個語句的執(zhí)行。然后我們同時(shí)執(zhí)行這兩個函數(shù) selectPizza()和selectDrink() 。
在第二個例子中我們需要處理未知數(shù)量的 Promise。處理這個問題非常簡單,我們只需要創(chuàng)建一個數(shù)組將所有 Promise 存入其中,使用 Promise.all() 方法并行執(zhí)行:
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 }
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
js原生map實(shí)現(xiàn)的方法總結(jié)
在本篇文章里小編給大家整理了關(guān)于js原生map實(shí)現(xiàn)的方法以及實(shí)例分析內(nèi)容,需要的朋友們可以參考下。2020-01-01JS實(shí)現(xiàn)的ajax和同源策略(實(shí)例講解)
下面小編就為大家分享一篇JS實(shí)現(xiàn)的ajax和同源策略的實(shí)例講解,具有很好的參考價(jià)值,希望對大家有所幫助2017-12-12Bootstrap select實(shí)現(xiàn)下拉框多選效果
這篇文章主要為大家詳細(xì)介紹了Bootstrap select實(shí)現(xiàn)下拉框多選效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12js中string和number類型互轉(zhuǎn)換技巧(分享)
下面小編就為大家?guī)硪黄猨s中string和number類型互轉(zhuǎn)換技巧(分享)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-11-11js實(shí)現(xiàn)登錄注冊框手機(jī)號和驗(yàn)證碼校驗(yàn)(前端部分)
這篇文章主要為大家詳細(xì)介紹了js實(shí)現(xiàn)登錄注冊框手機(jī)號和驗(yàn)證碼校驗(yàn)的前端部分代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09