Introduction

In a de-coupled environment, its not always easy to let various components communicate with each other. In order to make this task slightly easier AS3Commons offers the EventBus.

The EventBus is used as a publish/subscribe event mechanism that lets objects communicate with eachother in a loosely coupled way.

A dependency injection mechanism can be used to inject a shared EventBus instance into the necessary application components.

Multiple EventBus instances can operate independently of each other or can optionally be chained to allow for inter-application or -module communication.

EventBus listeners

There are a number of different ways to subscribe to events that are dispatched through the EventBus:

Global listeners using the IEventBusListener interface

The first is to listen to all events that are dispatched through the EventBus. This is possible through the EventBus.addListener() method. This method expects an IEventBusListener instance as an argument. The IEventBusListener interface looks like this:

public interface IEventBusListener {

 function onEvent(event:Event):void;

}

Every event dispatched by the EventBus will be passed into the onEvent method.

eventBus.addListener(listener);

Event type listeners

The second method is to only listen for events of a specific type. Use the EventBus.addEventListener() method for this task. The addEventListener() method expects two arguments, the first is a string representing the event type, and the second is a Function instance which will be invoked after every event dispatch of the specified type.

eventBus.addEventListener("myCustomEvent", listenerFunction);

Instead of a Function it is also possible to supply a proxy instead. This is what the addEventListenerProxy() method is for. Instead of a Function this expects a MethodInvoker instance. The MethodInvoker class is part of the as3-commons-reflect project.

var methodInvoker:MethodInvoker = new MethodInvoker();
methodInvoker.target = someInstance;
methodInvoker.method = "listenerMethodName";
eventBus.addEventListenerProxy("myCustomEvent", methodInvoker);

Event class listeners

The last option is to add a listener for events of a certain class. To get this to happen use the addEventClassListener() or addEventClassListenerProxy() methods. The same arguments apply to these as for their addEventListener() and addEventListenerProxy() neighbours, except they expect a Class instance instead of a type.

eventBus.addEventClassListener(MyCustomEvent, listenerFunction);

Removing listeners

All these methods naturally have a removal counterpart: removeListener(), removeEventListener(), removeEventListenerProxy(), removeEventClassListener() and removeEventClassListenerProxy().

To clear all types of registered eventlisteners at once simply call the removeAll() method.

Adding weak listeners

Each add*() method has an extra optional argument called useWeakReference that defaults to false. Setting this to true determines that the event listener will be added using a weak reference, which will make it eligible for garbage collection if no other non-weak references exist for it anymore.

eventBus.addListener(listener, true)

Filtering events using topics

Each add*() and remove*() method has one last extra optional argument: topic:Object.

By setting this argument to a non-null value determines that the listener will only be invoked if an event associated with the same topic is dispatched.

In the simplest case a topic is just a String:

eventBus.addListener(eventBusListener, false, "myTopic");

This topic however can also be a complex object, in which case the topic can be used as a specific context.

For instance, the object could be a security token in which case a listener can only be added for a certain secure events if it has received this token instance.

EventBus dispatchers

This is the easiest part, to dispatch an event through anEventBusinvoke either the dispatchEvent() or dispatch() methods. The former expects an Event instance while the latter expects a string that indicates a certain type of Event. This event will be created by this method and subsequently dispatched. For example:

eventBus.dispatchEvent(new MyCustomEvent("myCustomEventType"));

or:

eventBus.dispatch("myCustomEventType");

Dispatching events using topics

The dispatchEvent() and dispatch() methods have an optional topic argument, like the add*() and remove*() methods. Setting this topic will associate the dispatched event with the specified topic.

eventBus.dispatchEvent(new MyCustomEvent("myCustomEventType"), "myTopic");

EventBus event intercepting

To prevent events from passing through the EventBus, or to change their properties before they do, an IEventBus implementation accepts IEventInterceptor registrations.

This process is almost the same as for event listeners: Interceptors can be added globally, for specific event types or specific classes and for specific topics.

eventBus.addInterceptor(interceptor);
eventBus.addEventInterceptor("myCustomEvent", interceptor);
eventBus.addEventClassInterceptor(MyCustomEvent, interceptor);
//And for topic specific interception:
eventBus.addInterceptor(interceptor, "myTopic");
eventBus.addEventInterceptor("myCustomEvent", interceptor, "myTopic");
eventBus.addEventClassInterceptor(MyCustomEvent, interceptor, "myTopic");

The IEventInterceptor interface is a very simple one and looks like this:

public interface IEventInterceptor extends IEventBusAware {
	function get blockEvent():Boolean;
	function set blockEvent(value:Boolean):void;
	function intercept(event:Event, topic:Object=null):Event;
}

The intercept() method will be invoked for each event that passes through the EventBus and that adheres to the registration parameters of the IEventInterceptor.

To prevent the event to continue to be dispatched any further the blockEvent property can be set to true.

To replace the passed in Event instance by an alternative one, create that instance in the IEventInterceptor and return it in the intercept() method. If the Event doesn't need to be replaced either return the original instance or null.

An IEventInterceptor also receives a reference to the current EventBus each time its intercept() method is invoked, this allows an implementation to block certain events and replace them with a different instance. Events could be split up into multiple events, etc.

AS3Comons-eventbus offers the AbstractEventInterceptor to be used as a general base class for event interceptors.

EventBus event listener intercepting

To log, modify or block event listeners from being added an IEventListenerInterceptor can be registered with the EventBus.

This process is almost the same as for event listeners and interceptors: Listener interceptors can be added globally, for specific event types or specific classes and for specific topics.

eventBus.addListenerInterceptor(interceptor);
eventBus.addEventListenerInterceptor("myCustomEvent", interceptor);
eventBus.addClassListenerInterceptor(MyCustomEvent, interceptor);
//And for topic specific interception:
eventBus.addListenerInterceptor(interceptor, "myTopic");
eventBus.addEventListenerInterceptor("myCustomEvent", interceptor, "myTopic");
eventBus.addClassListenerInterceptor(MyCustomEvent, interceptor, "myTopic");

To prevent the listener from being added, simply set the blockListener property to true.

An IEventListenerInterceptor also receives a reference to the current EventBus each time its interceptListener() or interceptListenerProxy() method is invoked, allowing the interceptor to examine the EventBus before deciding to allow the listener to be added or not.

AS3Comons-eventbus offers the AbstractEventListenerInterceptor to be used as a general base class for eventlistener interceptors.

Event Post Processors

IEventInterceptors are invoked before any event listeners are executed, to add logic after the listeners have been executed there is the IEventPostProcessor interface. This is what it looks like:
public interface IEventPostProcessor extends IEventBusAware {
	function postProcess(event:Event, wasIntercepted:Boolean, topic:Object=null):void;
}
To add an instance of this interface that post processes every event sent throught the EventBus call the addPostProcessor() method.
Postprocessors for specific event types use the addEventPostProcessor() method, or for event classes, there is the addEventClassPostProcessor() method.

Static EventBus implementation

In a select few cases it might be easier to have one static eventbus in an application, for this purpose as3-commons-eventbus offers the StaticEventBus.

The StaticEventBus has static equivalents of all the IEventBus methods, so its usage is exactly the same.

EventBus chaining

The EventBus and SimpleEventBus itself are also an implementation of IEventBusListener. Therefore it is possible to chain multiple EventBus instances by adding an EventBus as a global listener to another EventBus.

In that case every event dispatched by the EventBus will be redispatched by the listening EventBus.

var eventBus1:EventBus = new EventBus();
var eventBus2:EventBus = new EventBus();
eventBus1.addListener(eventBus2, true);
eventBus1.dispatchEvent(new MyCustomEvent());
//MyCustomEvent will now also be dispatched through eventBus2...

Simple EventBus

The EventBus supports event interception, event listener interception and event postprocessors, if all of this is unneeded for a particular use-case there is also the SimpleEventBus.
The SimpleEventBus only supports the event publishing and subscribing using optional topics, so if there is a need for a lightweight eventbus, use the SimpleEventBus.

Like the StaticEventBus there is also a static equivalent for the SimpleEventBus, and that's the StaticSimpleEventBus.