June 6, 2022

EventAggregator in Javascript

I found an EventAggregator in Javascript here: https://davidwalsh.name/pubsub-javascript. It was nice and simple, but it was using a hidden javascript object to map the topic string to an array of listeners. I thought that a Map is more appropriate for this functionality so I changed the code to use one.

I do a lot of logging because this class tends to be the thing that fires all the domain events in a project, so logging here is very useful. I would like to convert it to Typescript some time. In my case the events were mostly sent to the Controller portion of MVC controllers which would decide if they needed to update their Models/Views


"use strict";

// Originally from here: https://davidwalsh.name/pubsub-javascript, 
// changed to use the Map class
// Allows objects to publish and subscribe to custom events/topics
// Changed to use a Map class rather than using an object
var eventAggregator = (function () {
    // maps a topic string to an array of subscriber/listener functions, 
    // each subscriber/listener function takes a single data object parameter 
    var _topicsMap = new Map();
    var _publishingEnabled = false;

    return {
        // topic is a string, listener is a function that takes a 
        // single data object parameter
        Subscribe: function (topic, listener) { 
            // Create the topic's object if not yet created, 
            // the key is the topic string, the value is an empty array (of listeners)
            if (!_topicsMap.has(topic)) _topicsMap.set(topic, []);

            // Add the listener to the array
            var index = _topicsMap.get(topic).push(listener) - 1;

            // Provide handle back for removal of topic
            return {
                Dispose: function () {
                    delete _topicsMap.get(topic)[index];
                }
            };
        },

        Enabled: function (isEnabled) {
            if (_publishingEnabled != isEnabled) {
                _publishingEnabled = isEnabled;
                let isEnabledStr = _publishingEnabled ? "enabled" : "disabled";
                console.log("Events publishing is " + isEnabledStr);
            }
        },

        Publish: function (topic, data) {
            // If publishing is disabled, or the topic doesn't exist,
            // or there's no listeners in the array, just leave
            if (!_publishingEnabled) {
                console.log("'" + topic + "' Events: Publishing is disabled");
                return;
            }
            else if (!_topicsMap.has(topic)) {
                console.warn("Events: Either the topic '" + topic + 
                "' doesn't exist, or there's no listeners in queue");
                return;
            }

            let dataSafe = (data) ? data : "";
            if (typeof dataSafe === 'string' || dataSafe instanceof String) {
                console.log("Events: event raised on topic: '"
                   + topic + "' with data '" + dataSafe + "'");
            } else {
               console.log("Events: event raised on topic: '"
                 + topic + "' with '" + JSON.stringify({ data }) + "'");
            }
            // Cycle through topics array, invoking each listener function
            _topicsMap.get(topic).forEach(function (item) {
                item(data != undefined ? data : {});
            });
        }
    };
})();