You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何含await的groupNotesPromise.map未执行完,console.log(groupNotes)就已打印?

问题:async函数中console.log为何先于异步操作完成前执行?

我编写了一个从MongoDB获取数据的async函数getGroup,代码如下:

const getGroup = async (req, res) => {
  const { id } = req.params
  try {
    const group = await Group.findOne({ _id: id });    
    const groupNotesPromise = await group.group_notes.map( async (fileId) => {
      const file = await File.findById(fileId)
      return file;
    })
    const groupNotes = await groupNotesPromise.map( async (notePromise) => {
      await notePromise.then(res => console.log(res))
    })
    console.log(groupNotes)
  } catch (error) {
    console.log(err.message);
    res.status(400).json({ err: err.message });
  }
}

但我发现,尽管groupNotesPromise.map内部使用了await,console.log(groupNotes)却先于该map的执行完成前就打印了。控制台输出结果如下:

[ Promise { <pending> }, Promise { <pending> } ]
{
  _id: new ObjectId("6496c645bf2bb491b931f082"),
  fileName: 'test',
  fileAwsReference: 'b4b71d06828d23d857b3611c2ce2480cf704f1fc1b9cb1879b563159631d162a',
  createdBy: new ObjectId("648bab3c8044efccc461ad1d"),
  createdIn: new ObjectId("648d7776284835591f536552"),
  __v: 0
},
{
  _id: new ObjectId("6496c9fafb91e60683fc812d"),
  fileName: 'waeewaea',
  fileAwsReference: '0e1f17f127f9a12a9e27d765bda1436297f0d58eb7d20f3e9dc8d39e64b58c5b',
  createdBy: new ObjectId("648bab3c8044efccc461ad1d"),
  createdIn: new ObjectId("648d7776284835591f536552"),
  __v: 0
}

请问这一现象的原因是什么?


原因分析与解决方案

核心原因:map结合async返回Promise数组,await无法直接等待数组完成

  • 第一个错误点:await作用于map返回的数组无效
    group.group_notes.map(async (fileId) => {...})会生成一个Promise数组,但await只能等待单个Promise对象,不能直接处理数组。所以groupNotesPromise本质就是这个未被处理的Promise数组,此处的await完全不起作用。

  • 第二个错误点:二次map生成新Promise数组,未等待全部完成
    你通过groupNotesPromise.map(async (notePromise) => {...})又生成了新的Promise数组,同样await这个数组不会等待所有Promise执行完毕,groupNotes直接就是处于pending状态的Promise数组,因此console.log(groupNotes)会优先打印出pending的Promise,而数组内部的异步操作(await notePromise.then(...))会在后续执行,所以文件数据会晚于groupNotes的打印结果出现。

  • 额外问题:catch块变量名不匹配
    catch的参数是error,但代码里写的是err.message,这会导致err未定义的报错。

修正后的代码

正确做法是使用Promise.all()等待所有Promise完成,简化后的代码如下:

const getGroup = async (req, res) => {
  const { id } = req.params
  try {
    const group = await Group.findOne({ _id: id });    
    // 生成Promise数组,无需在map前加await
    const groupNotesPromises = group.group_notes.map(fileId => File.findById(fileId));
    // 用Promise.all等待所有Promise完成,获取实际文件数组
    const groupNotes = await Promise.all(groupNotesPromises);
    console.log(groupNotes);
    // 可将结果返回给前端
    res.status(200).json({ group, groupNotes });
  } catch (error) {
    console.log(error.message);
    res.status(400).json({ err: error.message });
  }
}

关键说明

  • Promise.all()接收Promise数组作为参数,会等待所有Promise都resolved后,返回一个包含所有结果的数组,结果顺序与原Promise数组完全一致。
  • 无需额外包装async函数,因为File.findById本身就返回Promise,直接传入map即可。

内容的提问来源于stack exchange,提问作者tan shi yu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 10:50:03