2

How can i wait for callbacks inside a loop changing a variable, and still using asynchronicity?

The 2nd example is using async, in this case i don't know how to add a 2nd parameter sum to wait, in order to avoid a global var sum. Called like wait(sum,value); with a return value sum

wait is a representation for a complex function, which i use in my real-problem, so it can't be rewritten into "inline"-code and has to stay "function".

Example1:

var _ = require('underscore');
var arr = [1,2,3,4,5,6,7];
var sum = 0;

function wait(item,callback) {
  setTimeout(function() {
    callback(item);
  }, Math.ceil(Math.random()*1000));
}

var done = _.after(arr.length,function(value) {
  console.log('sum ='+value);
})

_.each(arr,function(itm) {
  wait(itm,function(value) {
    console.log('waiting... '+value);
    sum = sum + value;
  })
 // Please wait for the callback
  console.log(itm);
  done(sum);
});

Example2:

function asyncExample2() {
var async = require('async');
var arr = [1,2,3,4,5,6,7];

function otherWait(item, callback) {
    setTimeout(function() {
    callback(item); // call this when you're done with whatever you're doing
  }, Math.ceil(Math.random()*1000));
}

function wait(item, callback) {
  setTimeout(function() {
    otherWait(item,function() {
    console.log(item);
  });
  callback(item); 
  }, Math.ceil(Math.random()*1000));
}

function done() { console.log("sum = "+sum);};

var sum = 0;
async.forEach(arr, wait, done);
}

Desired Call:

sum = wait(sum,item) 

4 Answers 4

4

The easiest way to do this is putting done in the function wait. It makes done called only after the last callback is executed.

var arr = [1,2,3,4,5,6,7];
var sum = 0;

function wait(item,callback) {
  setTimeout(function() {
    callback(item);
    done(sum);
  }, Math.ceil(Math.random()*1000));
}

var done = _.after(arr.length,function(value) {
  console.log('sum ='+value);
})

_.each(arr,function(itm) {
  wait(itm,function(value) {
    console.log('waiting... '+value);
    sum = sum + value;
  })
  // Please wait for the callback
  console.log(itm);
  //done(sum);
});

Output:

enter image description here

Sign up to request clarification or add additional context in comments.

3 Comments

And make sure your array length doesn't change :), otherwise it breaks _.after
ty, is it possible to use "sum" as parameter in order to avoid globals, as described above?
It is possible, though. Add the parameter sum to wait, and make wait recursively calls itself. Indeed, it does not require each or loop. However personally I don't recommend that because complex function will make recursion elliptical.
2

Underscore is entirely synchronous so done(sum) would execute before wait has finished executing. For asynchronous operation don't use underscore.

Something simple like this should do what you want:

var sum = 0;
var waitNext = function(pos) {
    wait(arr[pos], function(value)) {
        if(pos < arr.length)
        {
            console.log('waiting... '+value);
            sum += value;
            waitNext(pos+1);
        }
        else
        {
            done(sum);
        }
    }
}
waitNext(0);

You could of course avoid using waitNext and just modify wait but this will work if wait is not your code. Not sure if you'd want sum += value inside the if or just before it since now there's an unnecessary waitNext call you could remove by tweaking the if condition's order.

Comments

2

You can really do a recursion version without global variable.

  var arr = [1,2,3,4,5,6,7];

  function wait(arr, max, callback, sum, done) {
    var item = arr.shift();
      setTimeout(function(){
        if(item) {
          sum[0] = callback(item, sum[0]);
          sum[1]++;
        }
        else
          sum[1] === max ? done(sum[0]) : wait(arr,max, callback, sum, done);
      }, Math.random()*1000);
      item && wait(arr, max,callback, sum, done);
  }

  function cb(item, acc) {
    console.log('waiting....' + item);
    return item + acc;
  }

  function done(sum) {
     console.log(sum);
  }

  wait(arr, arr.length,cb, [0, 0], done);

Output enter image description here

Comments

1

I am assuming that you are using setTimeout to implement the asynchronus behaviour, instead you can use a library like async which handles much more easily for you.

Ex of using async's each

var async = require('async');
var arr = [1,2,3,4,5,6,7];
var sum = 0;

async.forEach(arr, function(item, cb){
   sum = sum + item;
   cb();
}, function(err){
  console.log(sum);
});

4 Comments

my problem is that i have to wait in the loop body for an external funtion callback, kind of "sum = sum + item" would be a function that "waits".
I suspect that @inselberg wants fire the asynchronous calls one by one, in which case async.eachSeries would be suitable
@Plato. Yes even I think so now, we can be sure only he/she provide an example, without that it will be difficult what is intended.
@booth ... i ve added a 2nd example

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.