forEach不能与async/await搭配使用?

116 阅读1分钟

一、背景:

在公司项目写后端接口需要返回一串对象数组,每一项需要异步获取数据库中的关联数据,在forEach遍历中async/await实现不了

先来一个例子

const arr = [1,2,3,4]
arr.forEach(item=>{
  setTimeout((item)=>{
    // console.log(item)
    item+=1
  },0)
})

打印不出结果

再来一个async/await的例子

const promiseFn = (e) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(e)
    },500)
  })
}
const fun = ()=>{
  let arr = [1, 2, 3, 4]
  arr.forEach(async item => {
    const res = await promiseFn(item)
    console.log(res);
  });
  console.log('end');
}
//end
//1
//2
//3
//4

可见end先执行了 ,这样会导致什么结果?如果我在这进行某一些数据推进

const fun = ()=>{
  let arr = [1, 2, 3, 4]
  let newArr = []
  arr.forEach(async item => {
    const res = await promiseFn(item)
    newArr.push(res)
  });
  console.log(newArr);
  console.log('end');
}
fun()

打印结果是这样的:

//[]
//end

同样的操作如果用for

const fun = async () => {
  let arr = [1, 2, 3, 4]
  let newArr = []
  for (let i = 0; i <= arr.length - 1; i++) {
    const res = await promiseFn(arr[i])
    newArr.push(res)
  }
  console.log(newArr);
  console.log('end');
}
fun()

打印结果是正常的

//[1,2,3,4]
//end

上网查了资料发现,forEach中不可以进行异步操作,为什么?先上解决办法吧,毕竟把工作做好优先才能更好的摸鱼向leader汇报~

二、解决办法:

  1. 使用 for for-in for-of

    A.for百搭

    B.for-in使用object,如果用for-in遍历数组,可能会出现一些问题

    比如:

    const = [1,2,3]
    
    for (let index in arr) {
      let res = index + 1
      console.log(res)
    }
    //打印结果为01 11 21
    

    因为index索引为字符串型数字,不能直接进行几何运算

    使用for in会遍历数组所有的可枚举属性,包括原型

    C.for-of适用遍历数/数组对象/字符串/map/set等拥有迭代器对象(iterator)的集合,但是不能遍历对象,因为没有迭代器对象,但如果想遍历对象的属性,你可以用for in循环(这也是它的本职工作)或用内建的Object.keys()方法

  2. Promise.all + Map

const fun = async (data)=>{
  let newArr = []
  await Promise.all(data.map(async (item)=> {
    const res = await promiseFn(item);
    newArr.push(res)
  }));
  console.log(newArr);
  console.log('end');
}
fun(arr)
//[1,2,3,4]
//end

这样也是可以解决问题的,具体看业务需求

三、问题原因

完成任务之后我们再看看为什么会这样

咱们进入forEach源码看看

image.png

这里传入一个回调函数返回空值,还有可选的参数

网上随便复制一份手写forEach来进行参考

Array.prototype.myForEach = function (fn, thisValue) {
    if (typeof fn !== 'function') {
        throw new Error(`${fn} 不是一个函数`)
    }
    if ([null, undefined].includes(this)) {
        throw new Error(`this 是null 或者 undefined`)
    }
    const arr = Object(this)
    for (let i = 0; i < arr.length; i++) {
        fn.call(thisValue, arr[i], i, arr)
    }
}

所以就相当于fn是一个异步函数,但是在foreach里面包裹起来并没有在相应地加上await在每一个fn身上,就会导致这个问题的产生