31

我在双forEach循环中进行一些计算,如下所示:

array.forEach(function(element){
    Object.keys(element).forEach(function(key){

        /* some complex computations with asynchronous callbacks  */        

    });
});

someFunctionHere();

有没有办法让循环在执行功能之前先完成someFunctionHere( )?或者程序在继续之前知道循环是否完成的任何方式someFunctionHere( )......

我可能错过了一些论坛,但我发现的那些并没有帮助我实现我想要实现的目标,顺便说一下我在 NodeJS 中这样做的方式,我也在问是否有现有的库可以实现这一点。

我忘了把这个加起来还是应该是另一个问题?

有没有办法同步进行迭代,只有在当前迭代完成后才会进行下一次迭代?(非常遗憾)

谢谢你的帮助...

4

5 回答 5

45

看看 async.js,尤其是它的控制流语句,例如each whilstuntil

使用 async.js 你可以得到你想要的。

在您的实际情况下,您想要的是each函数(以前称为forEach),分别是eachSeries不并行运行单个迭代而是串行运行的函数(有关详细信息,请参阅eachSeries 的文档)。

举个例子:

async.eachSeries([ 2, 3, 5, 7, 11 ], function (prime, callback) {
  console.log(prime);
  callback(); // Alternatively: callback(new Error());
}, function (err) {
  if (err) { throw err; }
  console.log('Well done :-)!');
});

这将遍历素数数组并以正确的顺序打印它们,一个接一个,最后打印出来Well done :-)!

于 2013-03-01T16:17:06.487 回答
7

您可以将回调包装在倒计时闭包中:

var len = array.length;

function countdownWrapper(callback, callbackArgs) {
    callback(callbackArgs);
    if (--len == 0) {
        someFunctionHere();
    }
}

array.forEach(function(element){
    Object.keys(element).forEach(function(key){

        var wrappedCallback = countdownWrapper.bind(callback);
        /* some complex computations with asynchronous WRAPPED callbacks  */

    });
});

如果回调有不同数量的参数,你可以做一些小手术arguments而不是使用显式callbackArgs参数。

编辑您的编辑阐明了您希望在前一个计算完成回调后开始每个复杂计算。这也可以通过闭包轻松安排:

function complexOp(key, callback) { /* . . . */ }

function originalCallback(...) { /* . . . */ }

function doSomethingElse() { /* . . . */ }

var iteratorCallback = (function (body, callback, completion) {
    var i = 0, len = array.length;
    return function iterator() {
        callback.apply(null, arguments);
        if (++i < len) {
            body(array[i], iterator);
        } else {
            completion();
        }
    };
}(complexOp, originalCallback, doSomethingElse)); 

// kick everything off:
complexOp(array[0], iteratorCallback);
于 2013-03-01T16:22:32.827 回答
2

高性能解决方案:

在某些情况下,您可能希望/允许异步/并行处理数组,但希望在处理完 forEach 的所有成员后调用函数。例子:

var async = require('async');

async.forEach(array,function(elementOfArray, callback){
    //process each element of array in parallel 
    // ..
    // ..
    // .. 
    callback(); //notify that this iteration is complete
}, function(err){
 if(err){throw err;}
 console.log("processing all elements completed");
});

因此,您可以通过这种方式执行非阻塞 CPU 密集型操作。

注意:当您将eachSeries用于大型数组时,如此多的迭代回调可能会溢出堆栈。

在这里阅读: https ://github.com/caolan/async#control-flow

于 2016-03-11T10:02:52.737 回答
2

对于支持 Promise(或使用 polyfill)/nodejs 的浏览器,我自己实现了一个带有回调的 forEach 的同步版本,所以我将在这里分享。

回调必须返回一个承诺..

function forEachSync(array, callback) {
    let lastIndex = array.length - 1;
    let startIndex = 0;

    return new Promise((resolve, reject) => { // Finish all
        let functionToIterateWith = (currIndex) => {
            if (currIndex > lastIndex) {
                return resolve();
            } else {
                callback(array[currIndex]).then(() => {
                    functionToIterateWith(currIndex + 1);
                }).catch(err => reject(err));
            }
        }

        functionToIterateWith(startIndex);
    });
}
于 2017-06-19T08:52:28.300 回答
1
let dataObject = {};
Promise.all(objectArray.map(object => {
        return new Promise(resolve => {
            yourFunction(object, anotherParameter).then(returnValue => {
                dataObject[object] = returnValue;
                resolve();
            });
        });
    })).then(() => {
        return dataObject;
    });
于 2018-09-05T20:55:38.703 回答