Async functions inside forEach loops with js

Possible duplicate:
javascript: execute a bunch of asynchronous method with one callback

I have been struggling with this problem for several days, but I just can't find an elegant way to handle this. Here is the problem.

I am running a forEach loop and I need to know when everything is complete. Since the forEach loops are locked, it should be simple enough to just leave console.log after the forEach loop, which will work when the forEach loop is complete. However, if there is any function inside the forEach loop that is not synchronized, this whole system breaks up very quickly, and I do not know when the contents of the loop will be completed, except for very strange hacker counts and if statements. My question is is there an elegant way to handle this situation in javascript.

Here are some examples:

In this case, the forEach loop works fine because everything in the loop is synchronized.

 [1,2,3].forEach(function(num){ console.log("we're on " + num); }); console.log('done counting!') //=> we're on 1 //=> we're on 2 //=> we're on 3 //=> done counting! 

Here we begin to run into problems, but as a basic example:

 [1,2,3].forEach(function(num){ setTimeout(function(){ console.log(num) }, 200); }); console.log('done counting!'); //=> done counting! //=> we're on 1 //=> we're on 2 //=> we're on 3 

Although it makes sense that this happens, I now have an unsuccessful problem on my hands that I need a callback when we have finished counting, and there is no smooth way for me to have something other than something like this

 var counter = 0; var numbers = [1,2,3] var log_number = function(num){ console.log("we're on " + num); counter++; if (counter == numbers.length) { console.log("done counting!") } } numbers.forEach(function(num){ setTimeout(log_number(num), 200); }); //=> we're on 1 //=> we're on 2 //=> we're on 3 //=> done counting! 

Kindness is an awful lot of overhead. Is there a smoother way to do this? I checked the promises, pending and sequences, but I could not implement any of them in such a way as to make it more concise :(

To be clear, this is not setTimeout, in particular, this is my problem, it usually has async functions running inside the synchronization function - it was just the simplest example.

+5
javascript
source share
6 answers
 Array.prototype.forEachDone = function(fn, scope, lastfn) { for(var i = 0, c = 0, len = this.length; i < len; i++) { fn.call(scope, this[i], i, this, function() { ++c === len && lastfn(); }); } }; [1,2,3].forEachDone(function(num, i, arr, done){ setTimeout(function(){ console.log(num); done() }, 200); }, this, function() { console.log('done counting!'); }); 

it will do what you are looking for. forEachDone will take the same parameters as for Each, with an extra at the end, which is a callback that is called when the functions that apply to each element of the array are executed. the function that will be applied to each element of the array also accepts the same parameters as forEach, but also with an additional function that should be called when the function ends.

PS: I personally will never touch the prototype of my own object, but if you are looking for a beautiful one, this is it.

+7
source share
 var list = ["your", "mom"]; var count = 0; for(var i = 0; i < list.length; ++i) { getSomething(list[i], function() { if(++count == list.length) imDone(); }); } 

another way is like this

 var biscuits = ["cheddar", "butter milk"]; (function eatBiscuits(location) { eatIt(biscuits[++location], whenAllEaten); typeof biscuits[location] != "undefined" && eatBiscuits(location); }(-1)) function eatIt(biscuit, cb) { if (typeof biscuit == "undefined") { cb(); } else { stuffMyFace(); } } 
+4
source share

If you want to use lib, then async will be a good choice (works in browsers, as well as with node.js)!

 async.parallel([ function(){ ... }, function(){ ... } ], callback); async.series([ function(){ ... }, function(){ ... } ]); 
+1
source share

I usually use a small useful feature:

 ​function asyncCallback( iterations, onComplete ){ var total = iterations.length || iterations, count = 0; return function( func ){ func(); if ( ++count == total ) onComplete(); }; } var arr = [1,2,3]; var async = asyncCallback(arr, function(){ console.log('donezo!'); }); arr.forEach(function(num){ setTimeout(function(){ async(function(){ console.log(num); }); }, 200); }); 

+1
source share

There is really no smart way to do this. You need to use something like setTimeout , setInterval , Worker

0
source share
 [1,2,3,4,5,6,7,8].forEach(function(n){ setTimeout(function(){ console.log(arguments[0]); },1000*n,n); }); 

This works great for me.

-one
source share

All Articles