當前位置:
首頁 > 知識 > forEach中用async/await遇到的問題

forEach中用async/await遇到的問題

問題描述

var getNumbers = () => {
return Promise.resolve([1, 2, 3])
}
var multi = num => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (num) {
resolve(num * num)
} else {
reject(new Error("num not specified"))
}
}, 1000)
})
}
async function test () {
var nums = await getNumbers()
nums.forEach(async x => {
var res = await multi(x)
console.log(res)
})
}
test()

在這個例子中,通過 forEach 遍歷的將每一個數字都執行 multi 操作。代碼執行的結果是:1 秒後,一次性輸出1,4,9。這個結果和我們的預期有些區別,我們是希望每間隔 1 秒,然後依次輸出 1,4,9;所以當前代碼應該是並行執行了,而我們期望的應該是串列執行。

問題分析

JavaScript 中的循環數組遍歷

在 JavaScript 中提供了如下四種循環遍曆數組元素的方式:

  1. for
  2. 這是循環遍曆數組元素最簡單的方式

for(i = 0; i < arr.length; i++) {
console.log(arr[i]);
}

  1. for-in
  2. for-in 語句以任意順序遍歷一個對象的可枚舉屬性,對於數組即是數組下標,對於對象即是對象的 key 值。注意 for-in 遍歷返回的對象屬性都是字元串類型,即使是數組下標,也是字元串 「0」, 「1」, 「2」 等等。[不推薦使用 for-in 語句]

for (var index in myArray) {
console.log(myArray[index]);
}

  1. forEach
  2. forEach 方法用於調用數組的每個元素,並將元素傳遞給回調函數;注意在回調函數中無法使用 break 跳出當前循環,也無法使用 return 返回值

myArray.forEach(function (value) {
console.log(value);
});

  1. for-of
  2. for-of 語句為各種 collection 集合對象專門定製的,遍歷集合對象的屬性值,注意和 for-in 的區別

for (var value of myArray) {
console.log(value);
}

分析問題

在本例中 forEach 的回調函數是一個非同步函數,非同步函數中包含一個 await 等待 Promise 返回結果,我們期望數組元素串列執行這個非同步操作,但是實際卻是並行執行了。

forEach 的 polyfill 參考:MDN-Array.prototype.forEach(),簡單點理解:

Array.prototype.forEach = function (callback) {
// this represents our array
for (let index = 0; index < this.length; index++) {
// We call the callback for each entry
callback(this[index], index, this)
}
}

相當於 for 循環執行了這個非同步函數,所以是並行執行,導致了一次性全部輸出結果:1,4,9

async function test () {
var nums = await getNumbers()
// nums.forEach(async x => {
// var res = await multi(x)
// console.log(res)
// })
for(let index = 0; index < nums.length; index++) {
(async x => {
var res = await multi(x)
console.log(res)
})(nums[index])
}
}

解決問題

方式一

我們可以改造一下 forEach,確保每一個非同步的回調執行完成後,才執行下一個

async function asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array)
}
}
async function test () {
var nums = await getNumbers()
asyncForEach(nums, async x => {
var res = await multi(x)
console.log(res)
})
}

方式二

使用 for-of 替代 for-each。

for-of 可以遍歷各種集合對象的屬性值,要求被遍歷的對象需要實現迭代器 (iterator) 方法,例如 myObjectSymbol.iterator 用於告知 JS 引擎如何遍歷該對象。一個擁有 Symbol.iterator 方法的對象被認為是可遍歷的。

var zeroesForeverIterator = {
[Symbol.iterator]: function () {
return this;
},
next: function () {
return {done: false, value: 0};
}
};

如上就是一個最簡單的迭代器對象;for-of 遍歷對象時,先調用遍歷對象的迭代器方法 Symbol.iterator,該方法返回一個迭代器對象(迭代器對象中包含一個 next 方法);然後調用該迭代器對象上的 next 方法。

每次調用 next 方法都返回一個對象,其中 done 和 value 屬性用來表示遍歷是否結束和當前遍歷的屬性值,當 done 的值為 true 時,遍歷就停止了。

for (VAR of ITERABLE) {
STATEMENTS
}

等價於

var $iterator = ITERABLE[Symbol.iterator]();
var $result = $iterator.next();
while (!$result.done) {
VAR = $result.value;
STATEMENTS
$result = $iterator.next();
}

由此可以知道 for-of 和 forEach 遍曆元素時處理的方式是不同的。使用 for-of 替代 for-each 後代碼為:

async function test () {
var nums = await getNumbers()
for(let x of nums) {
var res = await multi(x)
console.log(res)
}
}

forEach中用async/await遇到的問題

打開今日頭條,查看更多圖片
喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 極客教程 的精彩文章:

程序員裁員兇猛:直播公司100多人就地被裁,接到消息時還在幹活
DOM探索之-認識DOM

TAG:極客教程 |