[英]map() function with async/await
有很多關於 async/await 在 javascript 映射函數中的行為的主題,但是,下面兩個示例中的詳細解釋會很好:
const resultsPromises = myArray.map(async number => {
return await getResult(number);
});
const resultsPromises = myArray.map(number => {
return getResult(number);
});
編輯:這當然是一個虛構的案例,所以剛剛開始辯論,映射函數為什么、如何以及何時應該等待 await 關鍵字。 解決方案如何修改這個例子,調用 Promise.all() 不是這個問題的目的。
getResult
是一個異步函數
其他答案很好地涵蓋了您的示例行為的細節,但我想嘗試更簡潔地說明它。
const resultsPromises = myArray.map(async number => {
return await getResult(number);
});
const resultsPromises = myArray.map(number => {
return getResult(number);
});
Array.prototype.map
同步循環遍歷數組並將每個元素轉換為其回調的返回值。
兩個示例都返回一個Promise
。
async
函數總是返回一個Promise
。
getResult
返回一個Promise
。
因此,如果沒有錯誤,您可以在偽代碼中將它們都視為:
const resultsPromises = myArray.map(/* map each element to a Promise */);
正如zero298 所述和alnitak 演示的那樣,這非常快速(同步)按順序啟動每個承諾; 但是,由於它們是並行運行的,因此每個 Promise 都會在他們認為合適的時候解決/拒絕,並且可能不會按順序解決(履行或拒絕)。
並行運行 Promise 並使用Promise.all
收集結果,或者使用for * 循環或Array.prototype.reduce
順序運行它們。
或者,您可以為我維護的可鏈接異步 JavaScript 方法使用第三方模塊來清理內容,並且 - 也許 - 使代碼符合您對異步映射操作如何工作的直覺:
const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); const getResult = async n => { await delay(Math.random() * 1000); console.log(n); return n; }; (async () => { console.log('parallel:'); await AsyncAF([1, 2, 3]).map(getResult).then(console.log); console.log('sequential:'); await AsyncAF([1, 2, 3]).series.map(getResult).then(console.log) })();
<script src="https://unpkg.com/async-af@7.0.12/index.js"></script>
當你想通過刪除.then()
回調來扁平化你的代碼或者你想隱式返回一個 Promise 時, async/await
很有用:
const delay = n => new Promise(res => setTimeout(res, n)); async function test1() { await delay(200); // do something usefull here console.log('hello 1'); } async function test2() { return 'hello 2'; // this returned value will be wrapped in a Promise } test1(); test2().then(console.log);
但是,在您的情況下,您沒有使用await
來替換.then()
,也沒有使用它來返回隱式 Promise,因為您的函數已經返回了 Promise。 所以它們不是必需的。
如果你想並行運行所有 Promises,我建議簡單地使用map()
返回getResult
的結果並生成一個 Promises 數組。 Promise 將按順序啟動,但最終將並行運行。
const resultsPromises = indicators.map(getResult);
然后你可以等待所有的承諾並使用Promise.all()
得到解決的結果:
const data = [1, 2, 3]; const getResult = x => new Promise(res => { return setTimeout(() => { console.log(x); res(x); }, Math.random() * 1000) }); Promise.all(data.map(getResult)).then(console.log);
但是,如果您想按順序運行每個 Promise 並等待前一個 Promise 解決后再運行下一個 Promise,那么您可以像這樣使用reduce()和async/await
:
const data = [1, 2, 3]; const getResult = x => new Promise(res => { return setTimeout(() => { console.log(x); res(x); }, Math.random() * 1000) }); data.reduce(async (previous, x) => { const result = await previous; return [...result, await getResult(x)]; }, Promise.resolve([])).then(console.log);
Array.prototype.map()
是一個轉換數組的函數。 它將一個數組映射到另一個數組。 其函數簽名中最重要的部分是回調。 回調在 Array 中的每個項目上調用,回調返回的是放入map
返回的新 Array 中的內容。
它不會對返回的內容做任何特別的事情。 它不會在項目上調用.then()
,它不會await
任何東西。 它同步轉換數據。
這意味着如果回調返回一個Promise
(所有async
函數都會這樣做),所有的 Promise 都將是“熱的”並且並行運行。
在您的示例中,如果getResult()
返回一個Promise
或本身是異步的,那么您的實現之間並沒有真正的區別。 resultsPromises
將由可能會或可能尚未解決的Promise
填充。
如果您想等待一切都完成后再繼續,您需要使用Promise.all()
。
此外,如果您只希望一次運行 1 個getResults()
,請使用常規for
循環並在循環內await
。
如果第一個代碼片段的目的是讓.map
調用等待所有 Promises 在返回之前得到解決(並讓這些回調按順序運行),恐怕它不會那樣工作。 .map
函數不知道如何使用async
函數來做到這一點。
這可以用下面的代碼來證明:
const array = [ 1, 2, 3, 4, 5 ]; function getResult(n) { console.log('starting ' + n); return new Promise(resolve => { setTimeout(() => { console.log('finished ' + n); resolve(n); }, 1000 * (Math.random(5) + 1)); }); } let promises = array.map(async (n) => { return await getResult(n); }); console.log('map finished'); Promise.all(promises).then(console.log);
您將在其中看到.map
調用在任何異步操作完成之前立即完成。
如果getResult
總是返回一個承諾並且從不拋出錯誤,那么兩者的行為將相同。
一些 Promise 返回函數可能會在返回 Promise 之前拋出錯誤,在這種情況下,將對getResult
的調用包裝在異步函數中會將拋出的錯誤轉換為被拒絕的 Promise,這可能很有用。
正如許多評論中所述,您永遠不需要return await
- 它相當於在承諾鏈的末尾添加.then(result=>result)
- 它(大部分)是無害的,但沒有必要。 只需使用return
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.