1
var o = new X();
o.setFooCallback1(function(result1){
});
o.setFooCallback2(function(result2){
});
o.foo("xxx");  

as you can see, when I call o.foo(), there're two callbacks will be fired with two results, result1 and result2, what I want to do is use pass result1 and result2 to my constructor function to create an object:

var y = new Y(result1, result2);  

But result1 and result2 come in different time(asynchronous), how could I handle this?
ps: the class X is from others' library, I can't modify it's implemention

BenMorel
  • 34,448
  • 50
  • 182
  • 322
wong2
  • 34,358
  • 48
  • 134
  • 179

3 Answers3

3

You need to implement what is known as the semaphore pattern

Here is a hands-on implementation:

var o = new X()
  , n = 0
  , result1, result2

function checkResults(){
    if (--n > 0) return;
    var y = new Y(result1, result2)
}

o.setFooCallback1(function(res){
    result1 = res
    checkResults()
})

o.setFooCallback2(function(res){
    result2 = res
    checkResults()
})

Or an object-oriented approach:

function Semaphore(callback){
    this.callback = callback
    this.count = 0
    this.args = []
}
Semaphore.prototype.check = function(){
    if (--this.count <= 0)
        this.callback.apply(null, this.args)
}
Semaphore.prototype.queue = function(){
    var self = this
    self.count++
    return function(res){
        self.args.push(res)
        self.check()
    }
}

var fooResults = new Semaphore(function(res1, res2){
    var y = new Y(res1, res2)
})

o.setFooCallback1(fooResults.queue())
o.setFooCallback2(fooResults.queue())

Note that it only captures the first callback arguments, but you can easily extend this to whatever you need.

BenMorel
  • 34,448
  • 50
  • 182
  • 322
Ricardo Tomasi
  • 34,573
  • 2
  • 55
  • 66
1

Basically you need to add another layer, that fires a callback after both callbacks from the first layer have executed, probably in a different object. The answer here is pretty good:

jQuery callback for multiple ajax calls

Community
  • 1
  • 1
Chris Cherry
  • 28,118
  • 6
  • 68
  • 71
0

Call the same function from, inside both callbacks, but only actually do anything once they have both returned, like this:

(function(){
  var y, r1, r2;

  function runWhenBothReturn() {
    if(r1 && r2) { //check both are set to a non-falsy value
      y = new Y(r1, r2); 
    }
  }

  var o = new X();

  o.setFooCallback1(function(result1){
    r1 = result1;
    runWhenBothReturn();
  });

  o.setFooCallback2(function(result2){
    r2 = result21;
    runWhenBothReturn();
  });

  o.foo("xxx"); 

})();

Notice that i have added a closure so that y, r1 and r2 doesn't become global variables, but stay local to the task.

If either r1 or r2 can have values that evaluate to false, then make sure to update the if sentence accordingly :)

BenMorel
  • 34,448
  • 50
  • 182
  • 322
Martin Jespersen
  • 25,743
  • 8
  • 56
  • 68