forEach中的await
forEach中的await
不知道你是否寫過類似的代碼:
function test() {
let arr = [3, 2, 1]
arr.forEach(async item => {
const res = await fetch(item)
console.log(res)
})
console.log('end')
}
function fetch(x) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(x)
}, 500 * x)
})
}
test()
復制代碼
我當時期望的打印順序是
3
2
1
end
結果現實與我開了個玩笑,打印順序居然是
end
1
2
3
為什么?
復制代碼
其實原因很簡單,那就是 forEach 只支持同步代碼。
我們可以參考下 Polyfill 版本的 forEach,簡化以后類似就是這樣的偽代碼
while (index < arr.length) {
callback(item, index) //也就是我們傳入的回調函數
}
復制代碼
從上述代碼中我們可以發現,forEach 只是簡單的執行了下回調函數而已,并不會去處理異步的情況。并且你在 callback 中即使使用 break 也并不能結束遍歷。
怎么解決?
一般來說解決的辦法有2種,for...of和for循環。
使用 Promise.all 的方式行不行,答案是: 不行
async function test() {
let arr = [3, 2, 1]
await Promise.all(
arr.map(async item => {
const res = await fetch(item)
console.log(res)
})
)
console.log('end')
}
復制代碼
可以看到并沒有按照我們期望的輸出。
這樣可以生效的原因是 async 函數肯定會返回一個 Promise 對象,調用 map 以后返回值就是一個存放了 Promise 的數組了,這樣我們把數組傳入 Promise.all 中就可以解決問題了。但是這種方式其實并不能達成我們要的效果,如果你希望內部的 fetch 是順序完成的,可以選擇第二種方式。
第1種方法是使用 for...of
async function test() {
let arr = [3, 2, 1]
for (const item of arr) {
const res = await fetch(item)
console.log(res)
}
console.log('end')
}
復制代碼
這種方式相比 Promise.all 要簡潔的多,并且也可以實現開頭我想要的輸出順序。
但是這時候你是否又多了一個疑問?為啥 for...of 內部就能讓 await 生效呢。
因為 for...of 內部處理的機制和 forEach 不同,forEach 是直接調用回調函數,for...of 是通過迭代器的方式去遍歷。
async function test() {
let arr = [3, 2, 1]
const iterator = arr[Symbol.iterator]()
let res = iterator.next()
while (!res.done) {
const value = res.value
const res1 = await fetch(value)
console.log(res1)
res = iterator.next()
}
console.log('end')
}
復制代碼
第2種方法是使用 for循環
async function test() {
let arr = [3, 2, 1]
for (var i=0;i
const res = await fetch(arr[i])
console.log(res)
}
console.log('end')
}
function fetch(x) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(x)
}, 500 * x)
})
}
test()
復制代碼
第3種方法是使用 while循環
async function test() {
let arr = [3, 2, 1]
var i=0;
while(i!==arr.length){
const res = await fetch(arr[i])
console.log(res)
i++;
}
console.log('end')
}
function fetch(x) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(x)
}, 500 * x)
})
}
test()
復制代碼
要想在循環中使用async await,請使用for...of 或者 for 循環, while循環
forEach支持async await
forEach 在正常情況像下面這么寫肯定是做不到同步的,程序不會等一個循環中的異步完成再進行下一個循環。原因很明顯,在上面的模擬中,while 循環只是簡單執行了 callback,所以盡管 callback 內使用了 await ,也只是影響到 callback 內部。
arr.myforeach(async v => {
await fetch(v);
});
復制代碼
要支持上面這種寫法,只要稍微改一下就好
Array.prototype.myforeach = async function (fn, context = null) {
let index = 0;
let arr = this;
if (typeof fn !== 'function') {
throw new TypeError(fn + ' is not a function');
}
while (index < arr.length) {
if (index in arr) {
try {
await fn.call(context, arr[index], index, arr);
} catch (e) {
console.log(e);
}
}
index ++;
}
};

相關推薦HOT
更多>>
SEO優化
SEO優化,1、合理的title、description、keywords:搜索對著三項的權重逐個減小,title值強調重點即可;description把頁面內容高度概括,不可過...詳情>>
2023-04-03 15:11:51
Python數據生產器
Python數據生產器,在軟件開發、測試或者數據分析過程中,有時候會需要一些測試數據。做測試的時候,需要模擬真實的環境,但是又不能直接使用真...詳情>>
2023-03-28 15:56:13
Java集合是什么?Java集合詳解
Java集合是Java編程語言中的一個重要概念,用于存儲、管理和處理數據。Java集合框架提供了一組接口和類,用于實現常見的數據結構,如列表、棧、...詳情>>
2023-03-20 19:12:47
js查找字符串中指定字符的位置
另外,如果要查找一個字符串中所有出現的指定字符的位置,可以使用indexOf()方法結合循環來實現。然后,我們使用循環遍歷字符串中的每一個字符...詳情>>
2023-03-10 14:06:35