Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

running code when two events have triggered

This is mostly a language-agnostic question.

If I'm waiting for two events to complete (say, two IO events or http requests), what is the best pattern to deal with this. One thing I can think of is the following (pseudo js example).

request1.onComplete = function() {
  req1Completed = true;
  eventsCompleted();
}

request2.onComplete = function() {
  req2Completed = true;
  eventsCompleted();
}

eventsCompleted = function() {

  if (!req1Completed || !req2Completed) return;
  // do stuff

}

Is this the most effective pattern, or are there more elegant ways to solve this issue?

like image 550
Evert Avatar asked Mar 14 '11 23:03

Evert


3 Answers

Before even going into the details, here's something neat that takes advantage of lambda functions off the top of my head:

function makeCountdownCallback(count, callback) {
    return function() {
        if (--count == 0)
            callback();
    };
}

request1.onComplete = request2.onComplete = makeCountdownCallback(2, function() {
    // do stuff
});

This obviously assumes that each event fires at most once, and doesn't take advantage of order.

like image 123
Matti Virkkunen Avatar answered Oct 07 '22 00:10

Matti Virkkunen


jQuery 1.5 has Deferreds: http://api.jquery.com/category/deferred-object/

You can easily set them up to call back only when some events have been triggered.

like image 23
Alex Wayne Avatar answered Oct 07 '22 01:10

Alex Wayne


Try #1: Here's a solution that doesn't require additional global variables:

request1.onComplete = function() {
    // register new handler for event2 here, overwriting the old one
    request2.onComplete = function() {
        // now they're both done
    }
}

request2.onComplete = function() {
    // register new handler for event1 here, overwriting the old one
    request1.onComplete = function() {
        // now they're both done
    }  
}

The handler for whichever event fires first will clear the other's old handler and assign a new one that includes the stuff you need to do after the completion of both events. Because we re-assign the second handler inside the handler of the first event (whichever that is), we always know we're done when that second handler finishes.

Try #2: Here's something that will work if each event type is different:

function onBoth(fn) {
    var last, done = false;
    return function(e) {
        if (last && last !== e.type && !done) {
            fn(); // done
            done = true;
        }
        last = e.type;
    }
}

For example, this won't alert "done" until the user has both scrolled and clicked:

var both = onBoth(function() {
    alert("done")
});

document.addEventListener("scroll", both, false);
document.addEventListener("click", both, false);

Try #3: The previous try can be modified to work for similar events:

function onBoth(fn) {
    var last, done = false;
    return function(curr) {
        if (last && last !== curr && !done) {
            fn(); // done
            done = true;
        }
        last = curr;
    }
}

...which should be used like this:

var check = onBoth(function() {
    alert("done")
});

request1.onComplete = function() {
    check(arguments.callee);
}

request2.onComplete = function() {
    check(arguments.callee);
}

Basically, this checks that two different callbacks have executed by storing a reference to the most recently executed call back. Its usage is a little clunky, but it gets the job done (i.e. it will still work if each of the events executes more than once).

like image 45
Wayne Avatar answered Oct 07 '22 02:10

Wayne