Monday, May 11, 2009

One event loop to rule them all

While experimenting with web workers in bespin I made a small change that makes working with them a lot nicer. In bespin we make heavy use of custom events to loosely bind things together in the application. So you see a lot of
bespin.publish("something:happened", { ... })
and
bespin.subscribe("something:happened", function () { ... })
The change I made is that you can now do the same thing in workers and it will Do The Right Thing (tm). Meaning you can subscribe to events that might be triggered by the UI or anywhere else from within the worker and also publish events that might be observed by handlers which live in the "main page". Overall this makes working with workers much more seamless and first class from a bespin perspective because it means that as long as one is not doing any direct UI work (as opposed to sending events to UI components) one can do everything from the worker without building custom interfaces.

For me working on bespin is really an experiment on how to design an event driven client side web applications (or postmodern web application). One of the things that felt kinda awkward up until now was how to know the right point in time to initialize a particular component. You might want to wait until the settings have been loaded (asynchronously via Ajax) and another component has been initialized. All these events are, of course, signaled by custom events but the order might be totally random and they might have already happened when we start looking for them. The solution I came up with (whether it is a good one remains to be seen) is to have a function that checks a list of events against all events that have fired already and then waits for the rest of the events to fire and finally calls a callback when this is done:
    // ** {{{ assertEvents }}} **
//
// Given an array of topics, fires given callback as soon as all of the topics have
// fired at least once
assertEvents: function (topics, callback) {
var count = topics.length;
var done = function () {
if(count == 0) {
callback();
}
};
for(var i = 0; i < topics.length; ++i) {
var topic = topics[i];
if (this.eventLog[topic]) {
--count;
} else {
bespin.subscribe(topic, function () {
--count;
done()
})
}
done();
}
},
Post a Comment