0.1.0 • Published 9 years ago

eventbus.js v0.1.0

Weekly downloads
1
License
ISC
Repository
github
Last release
9 years ago

EventBus.js

A minimalistic RFP library for event-driven architectures.

Rationale

There comes a time in all good programs when components or subsystems must stop communicating directly with one another. This is often achieved via the introduction of queues between the producers of data and the consumers/processors of that data. This architectural indirection ensures that important decisions can be made with some degree of independence, and leads to systems that are easier to understand, manage, monitor and change, and make better use of computational resources, etc.
Rich Hickey

That time came for one of my side projects. I could use some of the available alternatives, but I do side projects not for the sake of productivity, but to keep my saw sharp. So I decided to reinvent the wheel.

The main difference to the existing alternatives, is that the EventBus is intended to be a central component of a conglomerate of heterogeneous units, s.t. other components are wrapped around it. Whereas the majority of the existing solutions rather work with lots of streams, which are derived from any possible source of event sequences.

Basic Concepts

An event bus is a simple medium for message exchange among independent components. It understands two primitives: events and event handlers. Events have a type and a payload. Event handlers are registered per event type. That is, a handler registered for events of type X will "see" all events of type X emitted to the event bus, but no events of different types. An exception is the type "*", which mean "all events" (see below).

The event bus should be considered as a non-deterministic untimed medium. Hence, no assumptions are allowed about:

  • the handler execution order for an emitted event;
  • the absolute timing of the event processing.

An event bus can be forked (branched). Every child inherits those events of its parent, whose types were specified during branching. The branching can be used to implicitly implement logical transactions: a component which emits and consumes its own events should always use a separate event bus if possible.

Examples

In the following example, we create an event bus and subscribe two listeners to it.

var inputs = new EventBus();
inputs.subscribe("NUMBER", function(type, payload) {
    console.log("number", payload, "was emitted to the event bus");
});
inputs.subscribe("CHARACTER", function(type, payload) {
    console.log("character", payload, "was emitted to the event bus");
});

As next, we put some events onto the even bus.

inputs.put("NUMBER", 5);
inputs.put("CHARACTER", "c");

As expected, it results in two console messages:

number 5 was emitted to the event bus
character c was emitted to the event bus

Lets derive a child instance:

var numbers = inputs.branch(["NUMBER"]);

That is, we derive from the inputs bus a new bus specifying all event types, which will be forwarded to the new branch. In this case, it's only the event type NUMBER. Now we put a listener on the new bus.

numbers.subscribe("*", function(type, payload){
    var square = payload * payload;
    console.log("square number", square, "was computed.");
});

Now we put two events t the original bus.

inputs.put("NUMBER", 7);
inputs.put("CHARACTER", "d");

The output should be:

number 7 was emitted to the event bus
square number 49 was computed.
character d was emitted to the event bus

Since only the NUMBER event was propagated to the branched bus, we square the emitted number. Putting a number to the branched bus will not affect the parent:

numbers.put("NUMBER", 8);

results in:

square number 64 was computed.

Formalism

Signatures

Event

An event consists of a type and of a payload intended for the corresponding event handler. The payload can be any valid JavaScript value. For example, this is a valid bus event:

{
    type: "someId",
    payload: { a: 2, b: 3}
}

Event Handler

An event handler receives as argument the event type, the payload and the reference of the event bus instance, on which the handler is registered. This reference can be used by the handler to emit further events. The only output accepted by the event handler is a boolean value indicating its execution status (true is successful, any other value is considered as failed execution). This status is used by the Event Bus in case if the handler was configured to fire a bounded number of times. This is a valid event handler (which, doesn't handle anything, obviously):

function(eventType, payload) {
    return true
}

There is a special type event "*" which means "all events". All handlers subscribed to the type event "*" will receive all events sent to this bus.

API

Instantiation

var bus = new EventBus()

Emitting Events

bus.put({type: "someString", payload: someObject})

or more conveniently, just

bus.put(someString, someObject)

Where:

  • eventType is the type of the emitted event;
  • payload can be any JavaScript value.

Event Handler Registration

bus.subscribe(eventType, handlerFunction, expirationCounter)

Where:

  • eventType is the type of events, which will be trigger the event handler;
  • handlerFunction is the actual handler of type described above;
  • expirationCounter is a number defining how often the handler will be fired, before it expires: if no value specified, the handler will never expire. The expiration can be controlled by the handler itself: a call is only counted if the result of the call is not equal to false (i.e., undefined counts as a successful call).

Branching

var childBus = bus.branch()
var childBus2 = bus.branch(["Type1", "Type2"])

Thi first child will receive all parent's events, whereas the second on will receive only events of the specified types. All events put to the child are invisible for the parent.

bus.close()

Closes the bus. A closed bus deletes all of its event handlers and does not react on event emitting anymore. A closed child also notifies its parent about closing, which makes the parent deleting the child reference.

Merging

var mergedBus = bus.merge(anotherBus)

merges two bus instances into a new bus, which will receive events of both bus instances. The merged bus instance is considered as a child of the instance, on which you call the merge method.

License

Distributed under ISC license.