原文出處:https://dev.to/jamesliudotcc/how-to-use-async-await-with-map-and-promise-all-1gb5
我發現自己一直在使用帶有 async 和 await 的 map 函數。我需要重新學習如何使用 Promise 來解決這個問題,但是一旦我解決了這個問題,語法就變得非常漂亮且可讀。
JavaScript 的 async 和 wait 語法是 ES2017 中的新語法。我認為語法非常簡潔,因為它允許我編寫比承諾金字塔更短、更容易理解的程式碼,類似於承諾是回調地獄的改進。
但是當你想從一堆請求中取回一堆資料時會發生什麼? JavaScript 中沒有「await all」。這就是 Promises.all()
的用武之地。Promises.all() 收集一堆 Promise,並將它們匯總成一個 Promise。一旦所有內部承諾成功解決,Promise.all() 將返回已解決的承諾,其中所有內部承諾都已解決。為了讓事情變得更快,一旦任何內部 Promise 被拒絕,Promise.all() 就會拒絕。
重點是 Promise.all() 將一系列 Promise 轉換為單一 Promise,如果正常的話,它會解析為您想要的陣列。其他一切都只是細節。
不知何故,我花了很長時間才擺脫困境。這是我最終開始工作的程式碼,希望這有助於解釋。
假設您點擊一個 REST 端點並取得 REST 端點的 URL 陣列,其中包含您最終想要的內容。例如,您想從 Star Wars API 中找到有關電影 R2-D2 的一些資訊。無論出於何種原因,您都不能使用 SWAPI GraphQL。我們知道從網路取得是一個非同步操作,因此我們必須使用回呼、promise 或 async 和await 關鍵字。由於 R2-D2 出現在多部電影中,因此需要多次網路呼叫才能獲取所有電影。
首先,讓我們進行設定。讓我們只專注於我們正在開發的最小功能,因此我們將在命令列上使用 Node.js。 Node.js 沒有附帶 fetch,所以讓我們使用 npm 或 YARN 來安裝它。
npm install node-fetch --save-dev
或者
yarn add node-fetch --dev
async/await 的一個問題是,await 關鍵字只允許在 async 函數內部使用。在真實的程式中,您可能已經進行了足夠的封裝,以便您可以在使用await關鍵字的函數上新增async關鍵字,但在臨時檔案中,我們希望從封閉的上下文中抽像出來。但作為 Javascript 程式設計師,我們知道如何透過將我們想要的內容包裝在即時呼叫的函數表達式中來解決這個問題。
// prettier-ignore
const fetch = require('node-fetch')
// prettier-ignore
(async () => {
try {
let characterResponse = await fetch('http://swapi.co/api/people/2/')
let characterResponseJson = await characterResponse.json()
console.log(characterResponseJson)
} catch (err) {
console.log(err)
}
}
)()
現在我們已經有了基本的 async/await 語法,我們可以檢查回應以查看我們想要的電影欄位。它是一個 URL 陣列。
let films = characterResponseJson.films.map(async filmUrl => {
let filmResponse = await fetch(filmUrl)
let filmResponseJSON = filmResponse.json()
return filmResponseJSON
})
console.log(films)
當您執行此程式碼時,您會得到一組待處理的承諾。您需要新的“async”,否則箭頭函數內的等待將無法工作。如果你不「等待」獲取,你會得到一堆被拒絕的承諾,並且錯誤告訴你處理你的承諾拒絕。
但回想一下,「Promise.all()」接受一組 Promise 並將它們包裝成一個 Promise。所以我們包裝我們的“map”函數。我們已經知道一些處理單一 Promise 的好語法。我們可以‘等待’。
let characterResponse = await fetch('http://swapi.co/api/people/2/')
let characterResponseJson = await characterResponse.json()
let films = await Promise.all(
characterResponseJson.films.map(async filmUrl => {
let filmResponse = await fetch(filmUrl)
return filmResponse.json()
})
)
console.log(films)
為了進行比較,promise 中的等效程式碼如下所示:
fetch('http://swapi.co/api/people/2/')
.then(characterResponse => characterResponse.json())
.then(characterResponseJson => {
Promise.all(
characterResponseJson.films.map(filmUrl =>
fetch(filmUrl).then(filmResponse => filmResponse.json())
)
).then(films => {
console.log(films)
})
})
對我來說,第一組 .then().then()
非常語意化,我幾乎可以遵循 async/await 文法。但是一旦我們進入“Promise.all()”,僅使用 Promise 語法事情就開始變得難以遵循。無論我們要對影片執行什麼操作,都將取代“console.log”,並且在“.then”連結語法中,它已經埋藏了 3 級縮排。淺層程式碼是易於理解的程式碼。