Jungle Hunter
Jungle Hunter

Reputation: 7285

Know when all async calls initiated by .each() are done executing

I'm building something using node.js and I'm looping through all img tags using jQuery .each(). After all callbacks .each() initiates are done I'll have the value I need in foo.

var foo = -1;
window.each(function() {
  // abracadabra
  if (baz) foo = bar;
});
return foo; // this returns the incorrect foo as not all callbacks are complete

How do I get around this?

Edit

One way to do it is have an infinite loop that keeps checking the count of callbacks and when all are complete return foo. I feel that's a bit hack-ish and there would be something better?

var allCallbacksDone = false;
var foo = -1;
window.each(function(index, element) {
  // abracadabra
  if (baz) foo = bar;
  if (index == window.length - 1) allCallbacksDone = true;
});
while (!allCallbacksDone) {
  continue;
}
return foo;

Second Edit

abracadabra is a GET request to download remote images which is async.

Upvotes: 0

Views: 637

Answers (3)

Alnitak
Alnitak

Reputation: 339786

YOU CANNOT RETURN VALUES FROM FUNCTIONS WHICH DEPEND ON THE RESULT OF ASYNCHRONOUS CALLS

Sorry about the shouting, but it had to be said...

The best you can do is to have another callback be invoked once all of the others are done.

Anything else will result in busy loops., which will make the application non-responsive.

To resolve this (no pun intended), use jQuery's deferred objects:

var defArray = [];

window.each(function(index, element) {

    // create a deferred object and store it
    var d = $.Deferred();
    defArray.push(d);

    // trigger the image download 
    however you do it...

    // register a callback for the download completion
    // which "resolves" the deferred object
    someimg.onload = function() {
        d.resolve();

        // probably other actions needed here too
    }        
});

// now call _another_ function once _all_ of the deferreds are resolved
$.when.apply($, defArray).then(function() {
    // everything is done - do your calculations
});

Note that my first line still stands - you can't just wait for everything and then return the result - JS doesn't work that way.

Ideally you would create one addition deferred object, and your function return a promise based on that object, which your final callback will resolve with the result of the final calculation.

Upvotes: 4

Sune Rasmussen
Sune Rasmussen

Reputation: 954

Try this:

function check(callback) {
  var foo = -1, complete = false, count = window.length - 1, interval;
  window.each(function (index) {
    if (baz) foo = bar;
    complete = (index == count);
  });
  interval = setInterval(function () {
    if (complete) {
      clearInterval(interval);
      callback();
    }
  }, 20);
}

Call check() to run the .each() loop, providing a method as callback for when the task is complete.

Upvotes: 0

Reinstate Monica Cellio
Reinstate Monica Cellio

Reputation: 26143

Try this...

var foo = -1;  // Don't need this now
var count = window.length;
window.each(function() {
  // abracadabra
  count--;
  if (count == 0) complete(bar);
});

function complete(value) {
//  everything is complete and value == bar
}

Upvotes: 0

Related Questions