使用 forEach 时避免回调多次调用

Posted

技术标签:

【中文标题】使用 forEach 时避免回调多次调用【英文标题】:Avoid callback multi-invocation when forEach is used 【发布时间】: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。

感谢任何帮助!

【问题讨论】:

【参考方案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。 嗯,好的提示,谢谢。

以上是关于使用 forEach 时避免回调多次调用的主要内容,如果未能解决你的问题,请参考以下文章

运行 npm install 时多次调用回调

如何通过 foreach 函数避免在 dart Map 中使用 await 键

C++并发,同步设计,避免多次执行问题

foreach使用方法

节点 SSH2 客户端连接会话

为啥在初始页面加载时多次调用 React Ref 回调(作为箭头函数或内联函数)?