近來發現平時使用JavaScript 數組Array.forEach上存在誤區。
這裏描述一下使用場景:簡單來說就想對數組進行遍歷,遍歷中依次進行串行查詢。具體到場景就是:對數據庫數據進行讀取得到一組用戶id。然後又分別對該組用戶id進行遍歷 對每一個用戶id進一步查詢數據庫得到結果。這裏使用才發下在forEach語句裏面使用await 想要阻塞住forEach循環讓循環通過await實現串行效果何其愚蠢。
1 問題背景
這裏用一段代碼來描述forEach語法的誤用:
const userMap = new Map([["0001", "Bob"], ['0002', 'peter'], ['0003', 'John']]);
function getUserName(id){//模擬數據庫返回延遲
return new Promise((resolve, reject) => {
setTimeout(()=>{
const name = userMap.get(id)
return resolve(name);
}, 3000)
});
}
async function run(){
let users = ['0001','0002','0003']
let nameArray = [];
users.forEach(async (item) => {
const name = await getUserName(users[0])
nameArray.push(name);
});
console.log(nameArray);
}
run();
執行結果爲:
先打印出
nameArray:
[]
然後進行遍歷並調用了查詢接口。3秒後得到了3個查詢結果。
這並不是我預期想要的串行執行的效果,即前一個查詢完成後才執行後一個查詢,依次完成。
2 forEach 語法
這裏去MDN 下看下Array.forEach的文檔:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
forEach函數:
arr.forEach(callback(currentValue [, index [, array]])[, thisArg])
callback
爲數組中每個元素執行的函數,該函數接收一至三個參數:
currentValue
數組中正在處理的當前元素。
index 可選
數組中正在處理的當前元素的索引。
array 可選
forEach() 方法正在操作的數組。
thisArg 可選
可選參數。當執行回調函數 callback 時,用作 this 的值。
forEach()
方法按升序爲數組中含有效值的每一項執行一次 callback
函數,那些已刪除或者未初始化的項將被跳過(例如在稀疏數組上)。
文檔中說明的也非常清楚。forEach的作用僅爲**含有效值的每一項執行一次 callback
函數。**去阻塞callback 毫無意義。因爲可以理解爲數組每一項的callback都是並行的。並不存在先需要先第一項callback執行完成後,才遍歷第二項。
3 如何解決
使用for of 進行代替Array.forEach
async function run(){
let users = ['0001','0002','0003']
let nameArray = [];
// users.forEach(async (item) => {
// const name = await getUserName(item)
// console.log(name);
// nameArray.push(name);
// });
for (item of users){
const name = await getUserName(item)
console.log(name);
nameArray.push(name);
}
console.log('nameArray:');
console.log(nameArray);
}
輸出結果也正常了,間隔三秒打印一個查詢結果,最後返回整個數據
Bob
peter
John
nameArray:
[ 'Bob', 'peter', 'John' ]
4 more
這個問題實質上是JavaScript對想要串行執行和並行執行代碼用法掌握不夠熟練造成。這裏耶專門整理一篇文章來說明這個問題: