forEach循环异步操作:如何等待所有异步方法完成后返回值?
如何在forEach循环的所有异步方法完成后再返回结果?
你的代码现在存在两个明显问题:一是forEach里的userDB.getC()是异步操作,会在后台单独执行,但你直接就返回了空的backupBuild,这时候所有异步请求还没跑完;二是backupBuild[i].client会直接报错,因为初始backupBuild是空数组,backupBuild[i]根本不存在,没法直接赋值属性。
下面给你两种靠谱的解决方式:
方法一:用Promise.all + map代替forEach
forEach天生不支持异步任务的等待,我们可以用map遍历每个backup,把每个异步操作转换成Promise,再用Promise.all等待所有Promise完成后返回结果:
return backupModel.find({}).exec().then(backups => { // 遍历backups,每个元素对应一个返回处理后对象的Promise const backupPromises = backups.map(backup => { return userDB.getC(backup.id).then(user => { // 返回新的对象(也可以直接修改原backup,看你的业务需求) return { ...backup, client: user }; }); }); // 等待所有异步操作完成,返回处理后的完整数组 return Promise.all(backupPromises); });
Promise.all会等待所有传入的Promise都成功执行后,返回一个包含所有结果的数组,这时候返回的就是你需要的带client属性的备份数组了。
方法二:用async/await语法(更直观)
如果你的环境支持ES2017+,用async/await写起来会更清晰,注意不要在forEach里用await,要用for...of循环或者结合Promise.all:
// 写法1:用for...of循环逐个等待 return backupModel.find({}).exec().then(async backups => { const backupBuild = []; for (const backup of backups) { // 等待当前异步请求完成后再进行下一个 const user = await userDB.getC(backup.id); backupBuild.push({ ...backup, client: user }); } return backupBuild; }); // 写法2:结合async/await和Promise.all(并行处理,效率更高) async function getBackupsWithClients() { const backups = await backupModel.find({}).exec(); const backupBuild = await Promise.all( backups.map(backup => userDB.getC(backup.id).then(user => ({ ...backup, client: user })) ) ); return backupBuild; }
这里第二种写法是并行处理所有异步请求,比for...of逐个等待效率更高,推荐优先使用。
核心思路就是:必须等待所有异步操作都完成后,再返回最终的数组,Promise.all是处理多个异步任务的标准方案,而forEach因为不处理异步等待,所以完全不适合这种场景。
内容的提问来源于stack exchange,提问作者Hyro
相关产品推荐
相关产品推荐

