Compartilhar via


A JavaScript Event Bus

I was working on a Proof of Concept recently that needed to support loosely coupled Ajax components. The idea is a common one – components need to be able to hook into an existing page without detailed knowledge of how the page works, and immediately start responding appropriately. This is basically using loosely coupled events as an extensibility mechanism. It is by no means a new concept – just check out things like Prism for an example!

After some consideration we came up with an approach that I like – it is actually very simple, but works great. In JavaScript we have very few issues to do with avoiding sharing the wrong types, marshalling to UI threads, etc... so the basic implementation is incredibly simple. We end up with a central "Event Bus" component that receives events and publishes them to a list of listeners... something that Sys.Component and its EventHandlerList is perfect to assist us with. The code I ended up with is below;

Type.registerNamespace('Sample')

Sample.EventBus = function() {

    Sample.EventBus.initializeBase(this);

}

Sample.EventBus.prototype = {

    subscribe: function subscribe(eventType, callback) {

        this.get_events().addHandler(eventType, callback);

    },

    publish: function publish(eventType, arg) {

        var handler = this.get_events().getHandler(eventType);

        if (handler)

            handler(arg);

    }

}

Sample.EventBus.registerClass('Sample.EventBus', Sys.Component);

As you can see, this really is quite simple. We have the notion of an "eventType", that identifies the message type passing over the bus, and we have a single argument that can be used to carry a payload.

We could easily use strings to identify these event types, but I decided I'd like to use a static property – and a great way to register such a property is to pinch an idea from Microsoft Ajax's registerComponent method... so we end up with something like this;

Sample.EventBus.registerEventType =

        function registerEventType(target, eventType) {

    if (!target.Events)

        target['Events'] = {};

    target.Events[eventType] = eventType;

}

This is designed to allow us to "extend" some type definition with static properties that identify any events we're going to use. It seems logical that the extended type is also the type of the payload argument to the events... so all this now allows me to register my events like this;

// simple class to carry data values with event

Sample.Payload = function(dataValue) {

    this.data = dataValue;

}

// definition of event types relevant to Payload class

Sample.EventBus.registerEventType(Sample.Payload, 'Update');

Sample.EventBus.registerEventType(Sample.Payload, 'Click');

... and then to publish and subscribe to Event Bus messages like this;

bus.publish(Sample.Payload.Events.Update, new Sample.Payload('Some Data'));

... and this...

bus.subscribe(Sample.Payload.Events.Update, function(arg) {

    // perform some action

});

To make locating the bus simple, I have made it a Sys.Component, and retrieve it using a simple static helper function;

Sample.EventBus.get_Instance = function get_Instance() {

    return Sys.Application.findComponent('SampleEventBus');

}

Of course, the subscribers could be in any component or script – as long as they have a reference to the Event Bus and the Payload class (and you could remove this dependency and use strings if you didn't like it) they can listen and respond. The Event Bus could get more intelligent if we liked – perhaps logging, tracing, translation, or scheduled execution (to split up subscriber execution, hence avoiding blocking the UI thread)... and much more.

This brings the power of a loosely coupled event extensibility mechanism to an HTML page with JavaScript, and it could of course be used to compliment equivalent server side behaviour. Easy, huh? What do you think?

A really simple sample site is attached... try tabbing out of the text box, and then click the button. There are two components listening for different events. Try adding another to test my script!

 

AjaxEventBus.zip

Comments