Skip to content

Instantly share code, notes, and snippets.

@ahamid
Created November 8, 2011 07:17
Show Gist options
  • Save ahamid/1347206 to your computer and use it in GitHub Desktop.
Save ahamid/1347206 to your computer and use it in GitHub Desktop.
/**
* Allows for calling a method asynchronously and queueing pending callbacks to avoid
* race conditions.
*
* Example:
*
* function createUniverse(arg, callback) {
* // important stuff that should not run in parallel here
* callback(whatever);
* }
*
* You can now safely run this with queued callbacks:
*
* // or use bind/curry
* function inflationary(cb) { createUniverse("inflationary", cb); }
*
* multiplexCallback(inflationary, function() { ... thing one ... });
* multiplexCallback(inflationary, function() { ... thing two ... });
* multiplexCallback(inflationary, function() { ... thing three ... });
*/
function multiplexCallback(callbackable, callback) {
if (!callbackable.__multiplex_callback_list) {
callbackable.__multiplex_callback_list = [];
callbackable.__multiplex_callback_list.push(callback);
callbackable(function () {
for (var i = 0, l = callbackable.__multiplex_callback_list.length; i < l; i++) {
callbackable.__multiplex_callback_list[i]();
}
// delete processed callbacks
delete callbackable.__multiplex_callback_list;
});
} else {
callbackable.__multiplex_callback_list.push(callback);
}
}
function makeMultiplexedCallbackable(callbackable) {
return function(callback) {
return multiplexCallback(callbackable, callback);
}
}
describe("#multiplexedCallback", function() {
it("it should invoke all callbacks after asynchronous invocation", function() {
var times_called = {
red: 0,
green: 0
};
function paint_bikeshed(color, callback) {
// pretend important async stuff is here
times_called[color] = times_called[color] + 1;
setTimeout(callback, 1000);
}
var red_one = "";
var red_two = "";
var red_three = "";
var red_done = false;
var red_callbackable = makeMultiplexedCallbackable(function(cb) { paint_bikeshed("red", cb); });
var green_one = "";
var green_two = "";
var green_three = "";
var green_done = false;
var green_callbackable = makeMultiplexedCallbackable(function(cb) { paint_bikeshed("green", cb); });
runs(function() {
red_callbackable(function() { red_one = "red: sanding"; });
red_callbackable(function() { red_two = "red: priming"; });
red_callbackable(function() { red_three = "red: painting"; red_done = true; });
green_callbackable(function() { green_one = "green: sanding"; });
green_callbackable(function() { green_two = "green: priming"; });
green_callbackable(function() { green_three = "green: painting"; green_done = true; });
});
this.waitsFor(function() {
return red_done && green_done;
}, 20000);
runs(function() {
// method only called once, but all callbacks are called afterwards!
expect(times_called.red).toEqual(1);
expect(red_one).toEqual("red: sanding");
expect(red_two).toEqual("red: priming");
expect(red_three).toEqual("red: painting");
// method only called once, but all callbacks are called afterwards!
expect(times_called.green).toEqual(1);
expect(green_one).toEqual("green: sanding");
expect(green_two).toEqual("green: priming");
expect(green_three).toEqual("green: painting");
// the callback cache should be reset, so we should be able to execute it again
times_called.red = 0;
times_called.green = 0;
red_done = false;
green_done = false;
red_callbackable(function() { red_one = "red: something else entirely"; red_done = true; });
green_callbackable(function() { green_one = "green: something else entirely"; green_done = true; });
});
this.waitsFor(function() {
return red_done && green_done;
}, 20000);
runs(function() {
expect(times_called.red).toEqual(1);
expect(red_one).toEqual("red: something else entirely");
expect(times_called.green).toEqual(1);
expect(green_one).toEqual("green: something else entirely");
});
});
});
@ahamid
Copy link
Author

ahamid commented Nov 9, 2011

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment