【问题标题】:Avoid callback multi-invocation when forEach is used使用 forEach 时避免回调多次调用
【发布时间】:2018-10-31 09:31:48
【问题描述】:

我有一个处理数据数组的函数(第一个参数),一旦处理完成,它只调用一次回调函数(第二个参数)。我正在使用forEach 逐项处理数据,包括在一些检查中处理每个项目并将参数存储在数据库中。函数storeInDB() 进行存储工作,并在存储项目时使用回调(第二个参数)。

代码的第一种方法如下:

function doWork(data, callback) {
    data.forEach(function (item) {
        // Do some check on item
        ...
        storeInDB(item, function(err) {
           // check error etc.
           ...
           callback();
        });
    });
}

但是,这是错误的,因为callback 函数将被多次调用(与data 数组中的元素一样多)。

我想知道如何重构我的代码以实现所需的行为,即一旦存储工作完成,只需调用一次callback。我猜async 可以帮助完成这项任务,但我还没有找到合适的模式来组合 async + forEach。

感谢任何帮助!

【问题讨论】:

    标签: node.js asynchronous promise


    【解决方案1】:

    您可以使用诸如 async 之类的库来执行此操作,尽管我建议尽可能使用 Promise。对于您当前的问题,您可以使用计数器来确定已完成多少存储调用,并在总数完成时调用回调。

    let counter = 0;
    data.forEach(function (item) {
        // Do some check on item
        ...
        storeInDB(item, function(err) {
           // check error etc.
           counter++
           if (counter == data.length) {
             callback();
           }
        });
    });
    

    【讨论】:

      【解决方案2】:

      你也可以利用传递给函数的三个参数对每个数组方法执行

      function doWork(data, callback) {
      
          data.forEach(function (value,idx,arr) {
              // Do some check on item
              ...
              storeInDB(arr[idx], function(err) {
                  // check error etc.
                  ...
                  if ( (idx + 1) === arr.length ) {
                      callback();
                  }
              });
          });
       }
      

      【讨论】:

        【解决方案3】:

        如果storeInDB 函数返回一个promise,您可以将所有异步函数推送到一个数组并使用Promise.all。所有任务运行成功后,会调用回调函数。

        希望对你有所帮助。

        function doWork(data, callback) {
          let arr = [];
          data.map(function(itm) {
            // Do some check on item
            ...
            arr.push(storeInDB(item));
          });
          Promise.all(arr)
            .then(function(res) {
              callback();
            });
        }
        

        【讨论】:

        • 在这种情况下(以及任何其他情况,顺便说一句)你不应该使用forEach,而是使用map
        • storeInDB有回调时,不代表返回promise? @Bergi
        • 如果它需要一个回调,那通常意味着它返回一个promise。
        • 嗯,好的提示,谢谢。
        猜你喜欢
        • 1970-01-01
        • 2012-09-05
        • 2015-12-16
        • 1970-01-01
        • 1970-01-01
        • 2021-10-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多