【问题标题】:When does array.forEach execute in nodejs? [duplicate]array.forEach 什么时候在 nodejs 中执行? [复制]
【发布时间】:2020-12-21 06:16:53
【问题描述】:

我在 NodeJS 中创建了一个名为 test-for-each-loop.js 的文件,以尝试通过 mongoose db 包装器将记录保存到 mongodb 数据库。它看起来像这样:

const load = async () => {
    const content = [0,0,0,0,0];
    await content.forEach(async item=>{
      const job = new Job({data:item});
      await job.save();
    });
    process.exit();
};

load();

我从终端运行了这个命令:node test-for-each-loop.js。结果是没有任何东西保存到数据库中。我发现问题是await job.save() 没有执行。我的同事通过将代码更改为以下内容来修复我的代码:

const load = async () => {
    const content = [0,0,0,0,0];
    await Promise.all(content.map(async item=>{
      const job = new Job({data:item});
      await job.save();
    }));

    process.exit();
};

load();

我认为因为我在content.forEach() 前面放了一个await,所以它会在触发process.exit() 之前等待foreach 完成执行。有人可以向我解释为什么await content.forEach 实际上没有等待吗?


我注意到如果我从我的第一个脚本中删除process.exit(),那么await content.forEach 实际上会等待。怎么会?

【问题讨论】:

  • 它不会等待。 forEach 不返回任何内容,当然不是承诺。
  • 是的,相关问题现在有意义

标签: node.js asynchronous foreach


【解决方案1】:

Array.forEach 不返回承诺,它只会调用您提供的异步函数,而无需等待或 .then 之后。

虽然 promise.all 与 Array.map 有效,但我宁愿使用普通的 for 循环,如下所示:

const load = async () => {
    const content = [0,0,0,0,0];
    for(let item of content){
      const job = new Job({data:item});
      await job.save();
    }
    process.exit();
};

load();

【讨论】:

    【解决方案2】:

    forEach 立即同步执行。

    您给forEach 的回调却没有。 await 不起作用,因为为了让它起作用,forEach 必须返回一个承诺,但它不知道承诺,并且不返回任何东西。

    您的同事给出的修复很好,并且会并行执行所有操作。可以进一步简化:

    const load = async () => {
      const content = [0,0,0,0,0];
      await Promise.all(
        content.map(data=>(new Job({data})).save())
      );
      process.exit();
    };
    
    load();
    

    一般建议重写forEach 以使用简单循环。

    const load = async () => {
    
      const content = [0,0,0,0,0];
      for(const item of content) {
        const job = new Job({data:item});
        await job.save();
      };
      process.exit();
    };
    
    load();
    

    我建议将此作为“默认”解决方案,因为它与大多数人在使用 forEach 时的意图更相似。 mapPromise.all() 的解决方案。但是,如果 .save() 的每次迭代都允许并行运行,则速度会更快。

    Promise.all() 将在其中一个承诺出错时立即结束该过程。如果您不想这样,请改用Promise.allSettled()

    【讨论】:

      【解决方案3】:

      .forEach() 不支持异步。所以,它只是运行你的整个循环,在循环之后运行代码,然后在循环的每次迭代中开始的所有异步操作在循环完成后的某个时间完成。

      如果您插入process.exit(),它不会等待异步操作完成后再退出,因为您明确告诉nodejs 立即退出。如果您将process.exit() 排除在外,nodejs 会等待异步操作完成,然后自然退出,因为当仍有未完成的计时器、网络操作、文件操作等时,nodejs 不会自然退出......

      要实际排序您的异步操作,以便第二个操作在第一个操作完成之前不会开始,因此循环之后的代码在它们全部完成之前不会执行,您可以使用普通的 for 循环而不是.forEach().

      const load = async () => {
          const content = [0,0,0,0,0];
          for (let item of content) {
            const job = new Job({data:item});
            await job.save();
          });
          process.exit();
      };
      
      load();
      

      一个普通的for 循环是async 感知的,实际上会暂停循环,直到await 完成。

      事实上,我想说.forEach() 现在应该已经过时了,因为它的循环控制比普通的for 循环少得多(你不能中断、继续或从它的callback) 并且它不支持异步,所以它对 Promise 完全不友好。

      【讨论】:

        猜你喜欢
        • 2013-05-01
        • 2011-07-22
        • 1970-01-01
        • 2012-10-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-30
        相关资源
        最近更新 更多