user2736286
user2736286

Reputation: 563

JavaScript Publish / Subscribe Pattern: Showing Chain of Events?

Say I am using a pub/sub pattern with highly modularized code. When a function in one module sends out a 'publish', how I do make clear what functions in other modules are subscribing to that trigger?

For example:

// In module 1
function foo(data) {
    publish("trigger", data);
}

// In module 2
function bar(data) {}
subscribe("trigger", bar);


// In module 3
function baz(data) {}
subscribe("trigger", baz);

After reading module 1 and seeing that a 'publish' is been sent out, how would someone know where to look in my code for the subscribed callbacks?

An obvious solution might be to comment what modules contain functions that subscribe to the trigger, but that seems an impractical solution when dealing with a large number of publishes / subscribers.

I feel like I'm not fully understanding how to use the pub/sub pattern, since to me, the pattern seems to have no transparency whatsoever regarding function chains.

EDIT

My question pertains to making my code clear and easy to understand for someone reading my source code. I understand that during runtime, I could programmatically find the list of stored subscribers, by access the array of stored callbacks. But that does nothing for making my raw source code more easily understood.

For example, I current use a pattern like this:

// Main Controller Module
function foo(data) {
    module2.bar();
    module3.bar();
}

// In module 2
function bar(data) {}


// In module 3
function baz(data) {}

For starters, what is the proper term for this module? I thought it was a 'mediator' pattern, but looking here, it seems a mediator pattern is more like what I thought a pub/sub was?

With this pattern I feel the flow of my code is completely transparent. The reader doesn't need to dig around to find out what functions in other modules foo() might call.

But with the pub/sub pattern, once I send out the publish from foo(), it's like the reader has to somehow find the modules where the subscribed functions are.

But of course the downside of the above pattern is heavy dependency: module 1 needs both module 2 and 3 injected before it can call bar() and baz().

So I want to adopt the loose coupling of the pub/sub pattern, but I also want to keep the function flow transparency that the above pattern gives me. Is this possible? Or is this just the inherent trade-off of a pub/sub pattern?

To Mod:

Please delete question. I wrote this question poorly and would like to re-ask the question in a clearer manner. thanks.

Upvotes: 0

Views: 418

Answers (1)

HMR
HMR

Reputation: 39340

I thought the whole idea of publish subscribe or mediator is to loosely couple objects. Object1 doesn't need to know what happens who does what it is only concerned doing it's own thing and notifying whoever is interested that it's done doing what it does.

I register listeners only in a controller class and not all over the code. When the controller needs to do add or remove listeners then break up your process in steps that will inform the controller first (create appropriate events for it).

For example:

  1. We fetch data with XHR.
  2. Based on the data we create processors, processors are created with factory.
  3. Processors process data.
  4. Data is displayed.
  5. Process is finished.

In your controller you could have:

var Controller = {
  //fetch information and display it
  fetch : function(paramObj){
    var subscribeIds = [];
    //to have xhr listen to fetch can be done in an init function
    //  no need to add and remove every time but make a note here
    //  that it's registered in init and part of this process
    subscribeIds.push(Mediator.subscribe(xhr.fetch,"fetch"));
    //xhr will trigger dataFetched
    subscribeIds.push(Mediator.subscribe(Controller.initProsessor,"dataFetched"));
    //Controller will trigger displayFetched
    subscribeIds.push(Mediator.subscribe(dom.displayFetched,"displayFetched"));
    subscribeIds.push(Mediator.subscribe(Controller.displayedFetched,"displayedFetched"));
    paramObj.suscribeIds = subsribeIds;
    Mediator.trigger("fetch",paramObj);
  },
  initProsessor : function(paramObj){
    var processor = Processor.make(paramObj.data.type);
    paramObj.html = processor.process(data);
    Mediator.trigger("displayFetched",paramObj);
  },
  displayedFetched : function(paramObj){
    //You can decide the process is done here or take other steps
    // based on paramObj
    //You can unsubscribe listeners or leave them, when you leave them
    //  they should not be registered in the fetch function but rather
    //  in an init function of Controller with comments saying 
    //  basic fetch procedure
    Controller.cleanupListeners(paramObj.subscribeIds);
  },
  cleanupListeners : function(listenersIds){
    Mediator.unSubscribe(listenersIds);
  }
}

The code looks more complicated than needs to be. Someone looking at it may think why not let XHR make a Processor instance and tell it to process? The reason is that the Controller literally controls the flow of the application, if you want some other things to happen in between you can add them. As your application grows you'll add more and more processes and sometimes re factor functions to do less specific things so they can be better re used. Instead of possibly having to change your code in several files you now only re define the process(es) in the Controller.

So to answer your question as to where to find the listeners and where events are registered: in the controller.

If you have a single Mediator object you can have it dump listeners at any time, just write a dump method that will console.log the event names and functions.toString().

Upvotes: 1

Related Questions