簡體   English   中英

具有異步/等待功能的 map() 函數

[英]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);
});
  1. Array.prototype.map同步循環遍歷數組並將每個元素轉換為其回調的返回值。

  2. 兩個示例都返回一個Promise

    • async函數總是返回一個Promise

    • getResult返回一個Promise

    • 因此,如果沒有錯誤,您可以在偽代碼中將它們都視為:

const resultsPromises = myArray.map(/* map each element to a Promise */);
  1. 正如zero298 所述alnitak 演示的那樣,這非常快速(同步)按順序啟動每個承諾; 但是,由於它們是並行運行的,因此每個 Promise 都會在他們認為合適的時候解決/拒絕,並且可能不會按順序解決(履行或拒絕)。

  2. 並行運行 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。 所以它們不是必需的。

所有 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM