JavaScript forEach與async/await

近來發現平時使用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對想要串行執行和並行執行代碼用法掌握不夠熟練造成。這裏耶專門整理一篇文章來說明這個問題:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章