0
votes

I'm looking for an elegant way to build an event-driven architecture where modules (scripts) are completely independent and decoupled from each other and rely only on Mediator for communication. Let's consider typical example:

var SomeModule = function () {...};

SomeModule.init = function ()
{
    Mediator.register ('SomeEvent', this.onSomeEvent, this);
    Mediator.register ('OtherEvent', this.onOtherEvent, this);
};

SomeModule.onSomeEvent = function (data)
{
    var x = data.x;
    var y = data.y;
    // ........
    Mediator.dispatch ('ThirdEvent', {name: 'Liza', gender: 1});
    // now all modules registered for 'ThirdEvent' will get their callbacks executed
};

Typical Mediator operates as a dictionary routing calls from event name to according callback array to execute callbacks from. Now the problem lies in optimization: Mediator.dispatch (...) introduces dynamic objects and therefore polymorphic code which will remain slow and unoptimized when executed in V8. Some events will be fired 60 times per second and will have multiple callbacks, so it will surely benefit from optimizations.

What is the most elegant way to make this code monomorphic without introducing large boilerplate code for every new event?

EDIT: replaced .bind(this) with supplying this as context param as suggested in the comments.

1
Are you sure that object literals will not get optimized? One thing I can think of is to annotate your modules with something like //@event ThirdEvent(name, gender). These annotations would then be read by a code preprocessor which would generate constructors code for those automatically. Then in your code you could use Mediator.dispatch(new ThirdEvent('Liza', 'gender'));. Something else you should definitely be looking at is to de-bounce your event dispatching or callback processing when you can.plalx
@plalx Mediator.dispatch() will be called with different params object every time so it seems like V8 will not be able to create an optimized hidden class for that case. One time it will be Mediator.dispatch ("FirstEvent", {x: 1, y: 2, z: 3}), another time - Mediator.dispatch ("SecondEvent", {name: "Liza", gender: 1}).Vincent Pride
@VincentPride there doesn't need to be a single hidden class unless the mediator actually tries to use the object, but why would it use the object? It just passes it through, it doesn't use it, so it doesn't matter what hidden class it hasEsailija
@VincentPride note how he says "then all the code that interprets these configuration objects will go polymorphic and you will pay for this with performance": what that means is that code needs to use (interpret in their words) the object for its hidden class to matter. .splice, .push and so on don't use the object, they are "pass it around" methods.Esailija
@VincentPride here should be much faster implementation of the same pastebin.com/D1zzzt0M , I don't have time to create benchmarks for them but feel freeEsailija

1 Answers

1
votes

60 times per second for this sort of operation is nothing.

That said if you want to optimize it, the most obvious bottleneck here will be definitely calling a function created using the native .bind function. You should always (I am not joking about always)use a homemade bind as the first thing when optimizing:

function bind(fn, ctx) {
    return function() {
        return fn.apply(ctx, arguments);
    };
}

This doesn't do the same thing as Function.prototype.bind at all, which is the point: it does exactly what you would want bind to do and nothing more.