31

I am doing some computations inside a double forEach Loop something like this:

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

        /* some complex computations with asynchronous callbacks  */        

    });
});

someFunctionHere();

Is there a way for the Loop to finish first before doing the someFunctionHere( ) function? or any way that the program will know if the Loop is finish before continuing to someFunctionHere( ) ...

I may be missing some forums but the ones I found did not helped me of what I want to achieve and by the way I am doing this in NodeJS , also I am asking if there are existing libraries that can make this happen.

I forgot to Add this up or should this be an another question?

Is there a way to do the iteration synchronously that it will only proceed to the next iteration once the current iteration is done? (Sorry for this)

Thanks for any help...

megamoth
  • 695
  • 2
  • 12
  • 27
  • 1
    Yes, it's basically 100% guaranteed that "someFunctionHere" will be called before the asynchronous operations are finished. – Pointy Mar 01 '13 at 16:15
  • Regarding your edit: Golo's answer shows how you can use async.js to do what you want. (I believe that [`Clumpy.js`](http://www.tumuski.com/code/clumpy/overview/) can also be used for this.) My answer shows how you can "roll your own" solution if, for some reason, you cannot (or simply do not want to) use third-party libraries. – Ted Hopp Mar 01 '13 at 21:18
  • I have done the above problem using async.forEach() but it messed up my database since iterations are not called one iteration at a time(or if the iterations are done synchronously I might have a logical error in the code), so Golo's answer might do the same thing as I have done in asyn.forEach(), I mean from the term 'async' do iterations are not done synchronously? – megamoth Mar 02 '13 at 00:24
  • Or it could be done one at a time but does not wait for the current iteration to finish before proceeding to the next iteration, am I right? – megamoth Mar 02 '13 at 00:36

5 Answers5

46

Take a look at async.js, and especially its control flow statements, such as each whilst and until.

Using async.js you can get what you want to have.

In your actual situation what you want is the each function (which has formerly been known as forEach), respectively the eachSeries function which does not run the single iterations in parallel, but in serial (see the documentation for eachSeries for details).

To provide an example:

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 :-)!');
});

This will iterate over the array of prime numbers and print them in the correct order, one after each other, and finally print out Well done :-)!.

Golo Roden
  • 140,679
  • 96
  • 298
  • 425
  • Thanks for this one , but using the async library is not an option , I mean each iteration in the loop should really be synchronous since it is doing intensive db operations that it should only be one iteration at a time and I forgot to put this in the question also sorry for that – megamoth Mar 01 '13 at 18:30
  • The sample exactly shows this behavior: Each iteration within the loop runs synchronous. It's perfectly serialized. – Golo Roden Mar 01 '13 at 20:02
  • I have used async.forEach() in my previous solution before asking the question , so the iterations in async are done one at a time? because if so , I might have some logical errors in my code that I did not saw or it could be done one at a time but does not wait on the current iteration to finish before proceeding to the next iteration – megamoth Mar 02 '13 at 00:37
  • I guess you mean async.each(). This one runs all the tasks in parallel, but there is also async.eachSeries(), which should be perfectly fine for what you want to do. Check out https://github.com/caolan/async#eachseriesarr-iterator-callback for details. – Golo Roden Mar 02 '13 at 09:18
  • 1
    http://stackoverflow.com/questions/10390041/node-js-using-the-async-lib-async-foreach-with-object , this is the async.forEach() that I am talking about , maybe this is not really the solution but the description of the link that you gave me is what I want. – megamoth Mar 02 '13 at 10:16
  • As I have seen forEach was renamed to each. Hence we were talking about the same thing ;-). – Golo Roden Mar 02 '13 at 12:07
  • PS: If your issue was solved using my solution it would be great if you marked my entry as answer ;-). – Golo Roden Mar 02 '13 at 12:08
  • I will gladly select this as the answer if you can edit this to `async.eachSeries()` since it is the one that best suits as the answer(peace Golo) and thanks!! – megamoth Mar 02 '13 at 12:14
  • Okay, I've adjusted it ;-) – Golo Roden Mar 02 '13 at 13:17
  • if he uses an object (empty) instead of those values, am not sure if "Well done" will be shown. I don't understand why it has to be so bloody complicated to do such a simple iteration with NodeJs. btw, I am having same issues doing this too :( – Jack M. Aug 16 '14 at 09:15
  • @GoloRoden, thank you for this excellent snippet of code! This has really helped! – Sevak Avakians Oct 09 '15 at 20:50
  • 1
    Exactly what I needed! Thanks. :) – Telmo Trooper Nov 20 '17 at 21:34
7

You can wrap your callbacks in a count-down closure:

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  */

    });
});

If the call-backs have different number of arguments, you can do a little surgery on arguments instead of using an explicit callbackArgs parameter.

EDIT Your edit clarifies that you want to start each complex computation after the previous calculation completes it's callback. This can also be easily arranged through closures:

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);
Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
  • This is really of great help it might solve one of my problems and I still have to check this out , thanks! – megamoth Mar 01 '13 at 18:32
  • This basically is nothing else but what async.js gives you out of the box. It's just not "standardized", but "proprietary". – Golo Roden Mar 01 '13 at 20:02
  • @GoloRoden - Proprietary? That's preposterous. There are plenty of examples of this trick on the web, and it's easy enough to come up with it on the fly. (For the record, I did not copy the code in my post from anywhere.) The point of the post is that you don't need an entire package like [async.js](https://github.com/caolan/async) or [Clumpy.js](http://www.tumuski.com/code/clumpy/overview/); it's just a few lines of code. – Ted Hopp Mar 01 '13 at 20:21
  • That's why I put it in quotes. Of course it's not proprietary in its original meaning, but why re-invent the wheel for the 1000th time? And of course yo can do it on your own instead of using a library, but you can also do everything still in assembler. There's simply no meaningful reason why you would want to do it on your own. – Golo Roden Mar 01 '13 at 20:31
  • @GoloRoden - Okay, thanks for the clarification. Your first comment sounded like an accusation of some sort; perhaps I was being overly sensitive. Of course using a package like async.js or Clumpy.js is the preferred approach. However, there's a learning curve involved in using any third-party library. Also, sometimes including third-party libraries in your code can be problematic for non-technical reasons. It's always good to know how to do these things yourself. – Ted Hopp Mar 01 '13 at 21:05
  • No, and I am sorry if it sounded like that. It's just that I see it too often that people try to create everything from scratch than to use an established solution. Nevertheless, of course, it's important to understand that it's no "magic", and hence your answer is indeed quite helpful for learning :-). – Golo Roden Mar 01 '13 at 21:07
  • Yes I basically need closures but I do not know some tricky techniques to solve my problem but only if async solved my problem its ok for me to use it but unfortunately it did not because in the async library, iterations are done asynchronously (correct me if I am wrong) – megamoth Mar 02 '13 at 00:41
  • @TedHopp This is really good and informative but it is somewhat advance and requires a good understanding about javascript, but I tried the idea and it worked (I will not make this as the answer since there is a way to do it in async.js and I can't vote this up since my reputation is not enough hahaha and thanks!) and also using libraries makes the complexity, *in a programmer's point of view* , easy to handle – megamoth Mar 02 '13 at 12:18
  • I believe that "showing the principle behind how something works" is more educational and informative than just "use async.js". In this particular case we get to learn about CLOSURES in Javascript, which is a good thing to grok once and for all! In general, "just use a library" is poor advice in Javascript land, in my opinion, as much bloat is trendy and considered canonically correct. Much of this contemporary library bloat is based upon trying to alter what Javascripts behavior IS, and thus this answer addresses the truth of how Javascript really executes in a browser. Thank you @TedHopp – aaron p Jun 22 '20 at 11:51
2

HIGH PERFORMANCE SOLUTION:

There might be a case when you might want/allow to process the array asynchronously/parallely but want a function to be called after all members of forEach have been processed. Example:

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");
});

Hence, this way you perform non-blocking CPU intensive operations.

NOTE: When you use eachSeries for huge arrays, so many iterative callbacks might overflow the stack.

Read here: https://github.com/caolan/async#control-flow

Mohit Chawla
  • 181
  • 7
2

For browsers which support Promise (or using polyfill) / nodejs I've implemented my self a sync version of forEach with callbacks, so I'll just share that here..

The callback must return a promise..

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);
    });
}
jony89
  • 5,155
  • 3
  • 30
  • 40
2
let dataObject = {};
Promise.all(objectArray.map(object => {
        return new Promise(resolve => {
            yourFunction(object, anotherParameter).then(returnValue => {
                dataObject[object] = returnValue;
                resolve();
            });
        });
    })).then(() => {
        return dataObject;
    });
Erkam KUCET
  • 485
  • 8
  • 21