1 Comment

Backbone Event Best Practices

When our Backbone.js apps become complicated, we need to utilize Backbone’s EventAggregator. From the Marionette docs: “An event aggregator is an application level pub/sub mechanism that allows various pieces of an otherwise segmented and disconnected system to communicate with each other.”


// Three ways of creating an event bus in backbone
var marionetteVent = new Marionette.EventAggregator();
var backboneVent   = new Backbone.Wreqr.EventAggregator();
var vent           = _.extend({object}, Backbone.Events);

1. Make sure you need it.

It’s harder to follow the execution of an event-based program than of regular synchronous method calls, so make sure you understand why you need an event in the first place. It’s worthwhile to ask: if events weren’t available, what would I have to do to solve this problem? Consider a situation like this:


// parentView.js
updateData: function() {
  this.data.update();
	pageEventBus.trigger('dataUpdated');
}

// childView.js
initialize: function() {
  this.listenTo(pageEventBus, 'dataUpdated', this.handleDataUpdate);
},

handleDataUpdate: function() {
  this.render();
}

If the parent view has a reference to the child view, the code can be much simpler without events:


// parentView.js
updateData: function() {
  this.data.update();
  this.childView.render();
}

// no addition needed in childView.js

The event bus works best when a child view needs to communicate information to a parent or other view that’s out of its normal reach.

2. Listen to it.

Out of the two ways Backbone gives us to bind callbacks to events, prefer the use of listenTo rather than on:


// Handling an event with on
pageEventBus.on('advancedModeSelected', this.switchToAdvancedMode, this);

// Handling the same event with listenTo (preferred)
this.listenTo(pageEventBus, 'advancedModeSelected', this.switchToAdvancedMode);

Both methods make use of the same information, but the difference is where we’re recording it. Note that on is called on the event bus itself. We’re saying “event bus, whenever this event passes by, be sure to invoke this callback on this object.”

This works but is at risk of leaking memory. For instance, say the event bus has bound a callback to a child view, and then the parent view closes that child and creates a new view in its place. The old child view will still be retained in memory because the event bus will still have a reference to it (and will still be invoking callbacks on it in response to events). To avoid this problem, be sure to remove all previously-bound callbacks from any object that should be destroyed using off (a hard thing to do).

Alternatively just use listenTo, and the above problem will be managed for you. Note that listenTo is invoked on the object that would have otherwise been at risk of a memory leak. By keeping track of callback bindings on this object instead of just the event bus, Backbone can cleanup the objects bindings itself.

3. Name events well.

It’s hard enough having to ack to find all the places where an event is being handled, but it’s even worse when a poorly named event (e.g. stateChange) doesn’t help explain what going on at all. Let your event names give the reader of your code a good idea of the event’s meaning.

There’s a rule of thumb that says variable name length should be proportional to the size of the scope of the variable. (E.g. a loop iterator only needs a short name like i or j because it only exists inside of the loop, whereas a global variable like MAXIMUM_LOGIN_RETRIES_ALLOWED needs a longer name because it needs to be understood from anywhere in the codebase.) Event names are global (assuming your event bus is global), so make sure their names are descriptive and appropriate for the global context.

Have a convention, too. Backbone uses imperative names like change and add. However, I prefer past tense declarative names, like calculationsFinished and cartItemAdded. This style makes it clearer that the idea behind the event (like an item being added to the cart) has already happened, and we are now just responding (or reacting) to that event. Pick a style and be consistent.