jQuery plugin for Event Driven Architecture? - javascript

Are there any Event Driven Architecture jQuery plugins?
Step 1: Subscribing
The subscribers subscribe to the event handler in the middle, and pass in a callback method, as well as the name of the event they are listening for...
i.e. The two green subscribers will be listening for p0 events. And the blue subscriber will be listening for p1 events.
Step 2: The p0 event is fired by another component to the Event Handler
A p0 event is fired to the Event Handler
The event handler notifies it's subscribers of the event, calling the callback methods they specified when they subscribed in Step 1: Subscribing.
Note that the blue subscriber is not notified because it was not listening for p0 events.
Step 3: The p1 event is fired a component to the Event Handler
The p1 event is fired by another component
Just as before except that now the blue subscriber receives the event through its callback and the other two green subscribers do not receive the event.
Images by leeand00, on Flickr
I can't seem to find one, but my guess is that they just call it something else in Javascript/jquery
Also is there a name for this pattern? Because it isn't just a basic publisher/subscriber, it has to be called something else I would think.

You probably don't need a plugin to do this. First of all, the DOM itself is entirely event driven. You can use event delegation to listen to all events on the root node (a technique that jQuery live uses). To handle custom events as well that may not be DOM related, you can use a plain old JavaScript object to do the job. I wrote a blog post about creating a central event dispatcher in MooTools with just one line of code.
var EventBus = new Class({Implements: Events});
It's just as easy to do in jQuery too. Use a regular JavaScript object that acts as a central broker for all events. Any client object can publish and subscribe to events on this object. See this related question.
var EventManager = {
subscribe: function(event, fn) {
$(this).bind(event, fn);
},
unsubscribe: function(event, fn) {
$(this).unbind(event, fn);
},
publish: function(event) {
$(this).trigger(event);
}
};
// Your code can publish and subscribe to events as:
EventManager.subscribe("tabClicked", function() {
// do something
});
EventManager.publish("tabClicked");
EventManager.unsubscribe("tabClicked");
Or if you don't care about exposing jQuery, then simply use an empty object and call bind and trigger directly on the jQuery wrapped object.
var EventManager = {};
$(EventManager).bind("tabClicked", function() {
// do something
});
$(EventManager).trigger("tabClicked");
$(EventManager).unbind("tabClicked");
The wrappers are simply there to hide the underlying jQuery library so you can replace the implementation later on, if need be.
This is basically the Publish/Subscribe or the Observer pattern, and some good examples would be Cocoa's NSNotificationCenter class, EventBus pattern popularized by Ray Ryan in the GWT community, and several others.

Though not a jQuery plugin, Twitter released a JavaScript framework called Flight which allows you to create component-based architectures, which communicate via events.
Flight is a lightweight, component-based JavaScript framework from Twitter. Unlike other JavaScript frameworks which are based around the MVC pattern, Flight maps behavior directly to DOM nodes.
Flight is agnostic to how requests are routed or which templating library you decide to use. Flight enforces strict separation of concerns. Components in Flight do not engage each other directly.
They broadcast their actions as events and those components subscribed to those events can take actions based on them. To make use of Flight, you will need the ES5-shim and jQuery along with an AMD loader.
Flight - A Lightweight, Component-Based JavaScript Framework From Twitter

There are actually two of them:
Listen (faster): http://plugins.jquery.com/project/Listen
Intercept (more advanced): http://plugins.jquery.com/project/Intercept

Could this serve as a ligthweight message passing framework?
function MyConstructor() {
this.MessageQueues = {};
this.PostMessage = function (Subject) {
var Queue = this.MessageQueues[Subject];
if (Queue) return function() {
var i = Queue.length - 1;
do Queue[i]();
while (i--);
}
}
this.Listen = function (Subject, Listener) {
var Queue = this.MessageQueues[Subject] || [];
(this.MessageQueues[Subject] = Queue).push(Listener);
}
}
then you could do:
var myInstance = new MyConstructor();
myInstance.Listen("some message", callback());
myInstance.Listen("some other message", anotherCallback());
myInstance.Listen("some message", yesAnotherCallback());
and later:
myInstance.PostMessage("some message");
would dispatch the queues

This can easily be accomplished using a dummy jQuery node as a dispatcher:
var someModule = (function ($) {
var dispatcher = $("<div>");
function init () {
_doSomething();
}
/**
#private
*/
function _doSomething () {
dispatcher.triggerHandler("SOME_CUSTOM_EVENT", [{someEventProperty: 1337}]);
}
return {
dispatcher: dispatcher,
init: init
}
}(jQuery));
var someOtherModule = (function ($) {
function init () {
someModule.dispatcher.bind("SOME_CUSTOM_EVENT", _handleSomeEvent)
}
/**
#private
*/
function _handleSomeEvent (e, extra) {
console.log(extra.someEventProperty) //1337
}
return {
init: init
}
}(jQuery));
$(function () {
someOtherModule.init();
someModule.init();
})

A recent development is msgs.js "Message oriented programming for JavaScript. Inspired by Spring Integration". It also supports communication via WebSockets.
msgs.js applies the vocabulary and patterns defined in the 'Enterprise Integration Patterns' book to JavaScript extending messaging oriented programming into the browser and/or server side JavaScript. Messaging patterns originally developed to integrate loosely coupled disparate systems, apply just as well to loosely coupled modules within a single application process.
[...]
Tested environments:
Node.js (0.6, 0.8)
Chrome (stable)
Firefox (stable, ESR, should work in earlier versions)
IE (6-10)
Safari (5, 6, iOS 4-6, should work in earlier versions)
Opera (11, 12, should work in earlier versions)

I have used the OpenAjax Hub for its publish/subscribe services. It's not a jQuery plugin, but a standalone JavaScript module. You can download and use the reference implementation from SourceForge. I like the hierarchical topic naming and the support for subscribing to multiple topics using wildcard notation.

Related

Do I need to unsubscribe of jQuery events? [duplicate]

jQuery holds references to DOM nodes in its internal cache until I explicitly call $.remove(). If I use a framework such as React which removes DOM nodes on its own (using native DOM element APIs), how do I clean up jQuery's mem cache?
I'm designing a fairly large app using React. For those unfamiliar, React will tear down the DOM and rebuild as needed based on its own "shadow" DOM representation. The part works great with no memory leaks.
Flash forward, we decided to use a jQuery plugin. After React runs through its render loop and builds the DOM, we initialize the plugin which causes jQuery to hold a reference to the corresponding DOM nodes. Later, the user changes tabs on the page and React removes those DOM elements. Unfortunately, because React doesn't use jQuery's $.remove() method, jQuery maintains the reference to those DOM elements and the garbage collector never clears them.
Is there a way I can tell jQuery to flush its cache, or better yet, to not cache at all? I would love to still be able to leverage jQuery for its plugins and cross-browser goodness.
jQuery keeps track of the events and other kind of data via the internal API jQuery._data() however due to this method is internal, it has no official support.
The internal method have the following signature:
jQuery._data( DOMElement, data)
Thus, for example we are going to retrieve all event handlers attached to an Element (via jQuery):
var allEvents = jQuery._data( document, 'events');
This returns and Object containing the event type as key, and an array of event handlers as the value.
Now if you want to get all event handlers of a specific type, we can write as follow:
var clickHandlers = (jQuery._data(document, 'events') || {}).click;
This returns an Array of the "click" event handlers or undefined if the specified event is not bound to the Element.
And why I speak about this method? Because it allow us tracking down the event delegation and the event listeners attached directly, so that we can find out if an event handler is bound several times to the same Element, resulting in memory leaks.
But if you also want a similar functionality without jQuery, you can achieve it with the method getEventHandlers
Take a look at this useful articles:
getEventHandlers
getEventListeners - chrome
getEventListeners - firebug
Debugging
We are going to write a simple function that prints the event handlers and its namespace (if it was specified)
function writeEventHandlers (dom, event) {
jQuery._data(dom, 'events')[event].forEach(function (item) {
console.info(new Array(40).join("-"));
console.log("%cnamespace: " + item.namespace, "color:orangered");
console.log(item.handler.toString());
});
}
Using this function is quite easy:
writeEventHandlers(window, "resize");
I wrote some utilities that allow us keep tracking of the events bound to DOM Elements
Gist: Get all event handlers of an Element
And if you care about performance, you will find useful the following links:
Leaking Memory in Single Page Apps
Writing Fast, Memory-Efficient JavaScript
JavaScript Memory Profiling
I encourage anybody who reads this post, to pay attention to memory allocation in our code, I learn the performance problems ocurrs because of three important things:
Memory
Memory
And yes, Memory.
Events: good practices
It is a good idea create named functions in order to bind and unbind event handlers from DOM elements.
If you are creating DOM elements dynamically, and for example, adding handlers to some events, you could consider using event delegation instead of keep bounding event listeners directly to each element, that way, a parent of dynamically added elements will handle the event. Also if you are using jQuery, you can namespace the events ;)
//the worse!
$(".my-elements").click(function(){});
//not good, anonymous function can not be unbinded
$(".my-element").on("click", function(){});
//better, named function can be unbinded
$(".my-element").on("click", onClickHandler);
$(".my-element").off("click", onClickHandler);
//delegate! it is bound just one time to a parent element
$("#wrapper").on("click.nsFeature", ".my-elements", onClickMyElement);
//ensure the event handler is not bound several times
$("#wrapper")
.off(".nsFeature1 .nsFeature2") //unbind event handlers by namespace
.on("click.nsFeature1", ".show-popup", onShowPopup)
.on("click.nsFeature2", ".show-tooltip", onShowTooltip);
Circular references
Although circular references are not a problem anymore for those browsers that implement the Mark-and-sweep algorithm in their Garbage Collector, it is not a wise practice using that kind of objects if we are interchanging data, because is not possible (for now) serialize to JSON, but in future releases, it will be possible due to a new algorithm that handles that kind of objects. Let's see an example:
var o1 = {};
o2 = {};
o1.a = o2; // o1 references o2
o2.a = o1; // o2 references o1
//now we try to serialize to JSON
var json = JSON.stringify(o1);
//we get:"Uncaught TypeError: Converting circular structure to JSON"
Now let's try with this other example
var freeman = {
name: "Gordon Freeman",
friends: ["Barney Calhoun"]
};
var david = {
name: "David Rivera",
friends: ["John Carmack"]
};
//we create a circular reference
freeman.friends.push(david); //freeman references david
david.friends.push(freeman); //david references freeman
//now we try to serialize to JSON
var json = JSON.stringify(freeman);
//we get:"Uncaught TypeError: Converting circular structure to JSON"
PD: This article is about Cloning Objects in JavaScript. Also this gist contain demos about cloning objects with circular references: clone.js
Reusing objects
Let's follow some of the programming principles, DRY (Don't Repeat Yourself) and instead of creating new objects with similar functionality, we can abstract them in a fancy way. In this example I will going to reuse an event handler (again with events)
//the usual way
function onShowContainer(e) {
$("#container").show();
}
function onHideContainer(e) {
$("#container").hide();
}
$("#btn1").on("click.btn1", onShowContainer);
$("#btn2").on("click.btn2", onHideContainer);
//the good way, passing data to events
function onToggleContainer(e) {
$("#container").toggle(e.data.show);
}
$("#btn1").on("click.btn1", { show: true }, onToggleContainer);
$("#btn2").on("click.btn2", { show: false }, onToggleContainer);
And there are a lot of ways to improve our code, having an impact on performance, and preventing memory leaks. In this post I spoke mainly about events, but there are other ways that can produce memory leaks. I suggest read the articles posted before.
Happy reading and happy coding!
If your plugin exposes a method to programatically destroy one of its instances (i.e. $(element).plugin('destroy')), you should be calling that in the componentWillUnmount lifecycle of your component.
componentWillUnmount is called right before your component is unmounted from the DOM, it's the right place to clean up all external references / event listeners / dom elements your component might have created during its lifetime.
var MyComponent = React.createClass({
componentDidMount() {
$(React.findDOMNode(this.refs.jqueryPluginContainer)).plugin();
},
componentWillUnmount() {
$(React.findDOMNode(this.refs.jqueryPluginContainer)).plugin('destroy');
},
render() {
return <div ref="jqueryPluginContainer" />;
},
});
If your plugin doesn't expose a way to clean up after itself, this article lists a few ways in which you can try to dereference a poorly thought out plugin.
However, if you are creating DOM elements with jQuery from within your React component, then you are doing something seriously wrong: you should almost never need jQuery when working with React, since it already abstracts away all the pain points of working with the DOM.
I'd also be wary of using refs. There are only few use cases where refs are really needed, and those usually involve integration with third-party libraries that manipulate/read from the DOM.
If your component conditionally renders the element affected by your jQuery plugin, you can use callback refs to listen to its mount/unmount events.
The previous code would become:
var MyComponent = React.createClass({
handlePluginContainerLifecycle(component) {
if (component) {
// plugin container mounted
this.pluginContainerNode = React.findDOMNode(component);
$(this.pluginContainerNode).plugin();
} else {
// plugin container unmounted
$(this.pluginContainerNode).plugin('destroy');
}
},
render() {
return (
<div>
{Math.random() > 0.5 &&
// conditionally render the element
<div ref={this.handlePluginContainerLifecycle} />
}
</div>
);
},
});
How about do this when the user exits the tab:
for (x in window) {
delete x;
}
This is much better to do, though:
for (i in $) {
delete i;
}

JavaScript: Transforming events before passing it on to a callback

I am currently implementing a Bluetooth Library for Node.js that has support for macOS, Linux and Windows. To achieve cross platform compatibility I am using native, platform specific code (Objective-C, C++ & C) that use Node.js' EventEmitter and will trigger events whenever an asynchronous Bluetooth operation has completed or some other device has triggered an event for some reason.
The data format for these events look very differently and I would like to normalize them so users of my library will get there events in one single format, not matter which platform they are on.
Of course, I can attach event handlers to the native EventEmitters, transform and normalize the data and trigger Events with that normalized data in my own library.
The problem with that is that the users of the library will be able to attach - and - remove events to my library and what that means is that if they attached an event to my library, let's say to "deviceDiscovered" like so:
const myListener = (device) => { /* ... */ };
bluetoothLibrary.on("deviceDiscovered", myListener);
My implementation would have to look a little something like this:
class BluetoothLibrary {
nativeAdapter = getAdapterForCurrentOS();
on(event, callback) {
this.nativeAdapter.on(event, (data) => {
const normalizedData = this.normalize(data)
callback(normalizedData);
});
}
off(event, callback) {
this.nativeAdapter.off(event, callback)
}
normalize(data) {
/* ... */
}
}
The problem with this is if someone wants to remove their event listeners again, like so:
const myListener = (device) => { /* ... */ };
bluetoothLibrary.on("deviceDiscovered", myListener);
bluetoothLibrary.off("deviceDiscovered", myListener);
Because of my implementation, the listener never really would be removed because I never really attach the reference to the callback that has been passed to me to the native adapter's EventEmitter.
I am wondering if there is a way to transform or pipe events through some transformations on the way before passing them on to an event listener.

How to achieve multiple event fires & events memory (features of events & promises combined)

My requirements
Because of the asynchronous architecture of my applications I am looking for an 'event' system which has the following two two properties:
The events should be able to fire multiple times (possible with events, but not with promises)
When I start listening for an event that has already been fired, I want the listener to fire once immediately (as with promises)
The reason for 1. is that there are a lot of events (e.g. the updating of certain data) that I want to be able to fire multiple times. But I would like to combine this with 2. so that if an event has already fired upon adding the listener, this listener gets called immediately. This is because I'm not always sure (and I don't want to be sure) which piece of code gets run first.
My 'solution'
I have thought up the following solution. I'm using this in an AngularJS application therefore the AngularJS context, but the question is applicable for Javascript in general. Note that I simplified the code.
app.controller('AppCtrl', function(CustomEventEmitter){
// Broadcast an event. No listener added so nothing happens
CustomEventEmitter.broadcast('event');
// Add the event listener. Because the event allready fired, the listener gets called immediatly
CustomEventEmitter.on('event', function(){
console.log('Event emitted');
});
// Broadcast an other event
CustomEventEmitter.broadcast('event');
});
app.service('CustomEventEmitter', function(){
var
listeners = {},
memory = [];
this.broadcast = function(name){
// The normal broadcasting of the event to the listener
if(listeners[name]) {
listeners[name].forEach(function(listener){
listener();
});
}
// Push the event into the 'memory'
memory.push(name);
};
this.on = function(name, listener){
// The normal adding of the listener
if(!listeners[name]) {
listeners[name] = [];
}
listeners[name].push(listener);
// If an event is already in memory, call the listener
if(memory.indexOf(name) !== -1) {
listener();
}
};
});
My questions
My questions are these:
What is the 'best practice' solution for my requirements?
What do you think of my 'solution'?
Am I missing something completely obvious?
The reason for the last question is that it seems to me that this is a very common design paradigm but I seem unable to find the best way to solve this in simple and concise way.
Note
I understand this can be solved with the adding of extra code (e.g. before adding the listener, check in an other way if the event you are going to listen for already happened) but this is not what I'm looking for.
A "property" from bacon.js does exactly what you are asking for. This falls under the broader category of functional reactive programming (FRP). The most popular two libraries for this in JavaScript are probably
bacon.js
Reactive Extensions
Both of which provide the specific tool you're asking for, along with a vast array of alternatives.

Javascript PubSub Scope Issue

im having an issue with PubSub in Javascript.
im trying to figure out why $.subscribe is not printing the value. I assume its because of scope between $.publish and $.subscribe.
i would like to have other modules subscribe to the event. how would i do that?
i put an example on jsfiddle:
http://jsfiddle.net/Fvk2G/
window.MQ = (function (window, document, undefined) {
"use strict";
function MQ() {
testPubSub();
function testPubSub() {
$.publish("test");
}
}
return MQ
})(this, this.document);
var mq = new MQ();
$.subscribe("test", function () {
console.log("print value");
});
thanks
pete
You've set up a system that uses jQuery event handling for relaying messages, which is not in itself a bad idea. If you expect that it will save events that were triggered and report them to subsequent "subscribers", however, you've made an incorrect assumption about the semantics of the event mechanism. If a tree falls in the forest, the forest doesn't retain the sound until your hiking party arrives. Similarly, an event that's triggered with no listeners is just forgotten.
If you move your code that creates the "MQ" to after the subscription is done, then it works fine.

Listening and firing events with Javascript and maybe jQuery

In my JavaScript and Flex applications, users often perform actions that I want other JavaScript code on the page to listen for. For example, if someone adds a friend. I want my JavaScript app to then call something like triggerEvent("addedFriend", name);. Then any other code that was listening for the "addedFriend" event will get called along with the name.
Is there a built-in JavaScript mechanism for handling events? I'm ok with using jQuery for this too and I know jQuery makes extensive use of events. But with jQuery, it seems that its event mechanism is all based around elements. As I understand, you have to tie a custom event to an element. I guess I can do that to a dummy element, but my need has nothing to do with DOM elements on a webpage.
Should I just implement this event mechanism myself?
You have a few options:
jQuery does allow you to do this with objects not associated with the document. An example is provided below.
If you're not already using jQuery on your page, then adding it is probably overkill. There are other libraries designed for this. The pattern you are referring to is called PubSub or Publish/Subscribe.
Implement it yourself, as you've suggested, since this is not difficult if you're looking only for basic functionality.
jQuery example:
var a = {};
jQuery(a).bind("change", function () {
alert("I changed!");
});
jQuery(a).trigger("change");
I would implement such using MVVM pattern with knockjs library.
Just create an element, and use jquery events on it.
It can be just a global variable, doesn't even have to be connected to the DOM.
That way you accomplish your task easily and without any extra libs.
Isn't it possible to bind onchange events in addition to click events? For instance, if addFriend is called and modifies a list on the page, you could bind the change event to then invoke additional functionality.
$('#addFriendButton').click( function() {
// modify the #friendList list
});
$('#friendList').change( function() {
myOtherAction();
});
This is total Host independent, no need for jQuery or dom in this case!
function CustomEvents(){
//object holding eventhandlers
this.handlers_ = {};
}
//check if the event type does not exist, create it.
//then push new callback in array.
CustomEvents.prototype.addEventListner = function (type, callBack){
if (!this.handlers_[type]) this.handlers_[type] = [];
this.handlers_[type].push(callBack);
}
CustomEvents.prototype.triggerEvent = function (type){
//trigger all handlers attached to events
if (!this.handlers_[type]) return;
for (var i=0, handler; handler = this.handlers_[type][i]; i++)
{
//call handler function and supply all the original arguments of this function
//minus the first argument which is the type of the event itself
if (typeof handler === "function") handler.apply(this,arguments.slice(1));
}
}
//delete all handlers to an event
CustomEvents.prototype.purgeEventType = function(type){
return delete this.handlers_[type];
}
test:
var customEvents = new CustomEvents();
customEvents.addEventListner("event A", function(arg){alert('Event A with arguments' + arg);));
customEvents.triggerEvent("event A", "the args");
EDIT added arguments passing

Categories

Resources