如何避開async/await地獄

在這裏插入圖片描述
async/await把我們從回調地獄中解放了出來,但是隨之而來的是大家對它的濫用,導致了async/await地獄的誕生。
在這篇文章裏我將會解釋什麼是async/await地獄以及分享一些方法去避開它。

什麼是async/await地獄?

當我們在編寫JavaScript異步代碼的時候,常常會在一個接着一個函數調用前添加await關鍵字,影響了函數的調用。因爲在一般情況下,下一個函數的調用並不依賴於前一個函數的調用,但是正是因爲添加了await關鍵字,我們仍需要等待前一個函數調用完畢才能調用下一個函數。

async/await地獄舉例(1)

假設我們要寫一段代碼用於購買披薩和飲料,這段代碼如下所示:


從表面上看這段代碼沒有問題並且能夠運行,但是這不是一個好的方法因爲沒有考慮到併發問題。接下來讓我們仔細分析一下這段代碼然後明確其中問題所在。

解析

我們把這段代碼包裹在了一個異步立即執行函數裏,下列事件會依次發生:

  • 獲得披薩的列表.
  • 獲得飲料的列表.
  • 在披薩列表中選擇披薩.
  • 在飲料列表中選擇飲料.
  • 把選擇的披薩加入購物車
  • 把選擇的飲料加入購物車
  • 確認訂單

問題在哪裏?

就像我在前面提到的那樣,所有的代碼都是一行接着一行執行的,這裏不存在併發執行的情況。讓我們仔細想想,爲什麼我們在獲取飲料列表之前需要等待披薩列表的返回?我們應該嘗試同時獲取飲料和披薩的列表。然而,當我們需要選擇披薩的時候,我們需要先獲取披薩的列表。飲料也是如此。
因此我們可以得出結論:披薩相關的工作和飲料相關的工作能夠同時執行,但是披薩相關的每一步工作需要按次序執行。

錯誤示範例子(2)

下面這段JavaScript代碼會獲取購物車裏面的物品,然後發送確認訂單的請求。


在這種情況下,for循環在執行下一輪循環之前需要等待當前的sendRequest()函數執行完成。然而,實際上我們不需要等待,我們希望儘可能快的發送所有請求然後等待他們都完成執行。
現在,我希望大家能夠清晰的理解什麼是async/await地獄以及它們對程序的性能影響的嚴重性。現在,我要問一個問題。

如果我們忘記了await關鍵字會怎樣?

如果你忘記在異步函數調用的前面添加await關鍵字,這時函數開始執行了,這意味着await並不是函數執行的必要條件。這個異步函數會返回一個promise,這個promise我們可以在之後使用。


另一個後果就是編譯程序不知道你需要等待這個函數執行完成,因此編譯程序會在這個異步任務還沒有完成的時候退出這個程序,所以我們需要await關鍵字。
promise有一個有趣的性質是:你可以在前面的代碼得到這個promise, 然後在後面的代碼中等待這個promise的完成。這是從async/await地獄中解脫的關鍵。

正如你所見到的那樣,doSomeAsyncTask()返回了一個promise。這個時候,doSomeAsyncTask()已經開始執行了。爲了得到這個promise的結果值,我們可以在這個promise前面添加await,JavaScript將會立刻停在這裏不再執行下一行代碼,直到獲得了這個promise的返回值,再執行下一行代碼。

如何避開 async/await 地獄?

依照以下步驟來逃離async/await地獄。

找出所有依賴其他代碼來執行的代碼

在我們第一個例子裏面,我們在選擇披薩和飲料。因而我們得出結論:在選擇披薩之前,我們需要獲得披薩的列表;在把披薩加入到購物車之前,我們需要選擇披薩。我們可以認定這三個步驟是互相依賴的,我們無法在前一個步驟完成之前執行下一個任務。但是,如果我們退一步來看,就會發現選擇披薩並不依賴於選擇飲料,我們可以同時對他們進行選擇。在這一件事情上,機器能比人類做得更好。
所以我們已經發現了一些語句依賴於其他的語句執行而另外一些語句並不依賴於其他。

把相互依賴執行的語句整合在異步函數裏面

正如我們所看到的,選擇披薩需要以下幾個互相依賴的語句:獲得披薩列表, 選擇其中一個披薩以及添加到購物車中。我們應該把這些語句整合在一個異步函數裏面。這樣我們將會得到兩個異步函數,selectPizza()和
selectDrink()。

併發的執行這些異步函數

我們將利用event loop的優勢來併發的執行這些非阻塞異步函數。常用的一個方法是先返回promise然後使用Promise.all方法。

改正錯誤示範的例子

根據前面提到的三個步驟,我們把他們運用的我們的例子中。


現在,我們已經把這些代碼整合到兩個函數中。在每一個函數裏面,每一條代碼的執行依賴於前一條代碼的執行。然後我們併發的執行selectPizza()selectDrink()
對於例2,我們需要解決未知數量的promise,解決這種情況非常簡單:我們只需要創建一個數組然後把promise存入其中,然後使用Promise.all()方法,就能夠併發的等待所有的promise返回結果。

希望本文能幫助您看透async/await,並幫助您改進應用程序的性能。

❤️ 看之後

  • 點贊,讓更多的人也能看到這篇內容(收藏不點贊,都是耍流氓 -_-)
  • 關注公衆號「新前端社區」,號享受文章首發體驗!每週重點攻克一個前端技術難點。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章