The Event Aggregator pattern
provides centralized events, thus simplifying producer/consumer registration/interaction.
Its implementation using Backbone.Events
looks something like this (assuming node.js)
var _ = require("underscore"), Backbone = require("backbone"), EA, Listener; EA = _.extend({}, Backbone.Events); Listener = Backbone.Model.extend({ initialize: function () { var eventAggregator = this.get("eventAggregator"); this.listenTo(eventAggregator, "something:ready", this.onSomethingReady); }, onSomethingReady: function () { console.log("something ready from " + this.cid); } }); var instance = new Listener({eventAggregator: EA}); EA.trigger("something:ready");
The idea is that we pass the aggregator to the listeners and they
are responsible for registering for any event of interest. The above outputs
something ready from c1
So, what happens when we run the following?
var instance = new Listener({eventAggregator: EA}); EA.trigger("something:ready"); instance = new Listener({eventAggregator: EA}); EA.trigger("something:ready");
something ready from c1 something ready from c1 something ready from c3
Garbage collection is not an excuse to forget about memory management
Yep! The Listener instance with cid c1 is still around, listening for events.
Can you spell memory leak? Turns out, the garbage collector can not collect this object,
because we passed a reference to the event aggregator the moment we registered using listenTo
.
It will not be garbage collected until the event aggregator itself goes out of scope.
Avoiding it
Here are two patterns that can help
For recurring events, Stop listening before disposing an object
var instance = new Listener({eventAggregator: EA}); EA.trigger("something:ready"); if (instance) { instance.stopListening(); } instance = new Listener({eventAggregator: EA}); EA.trigger("something:ready");
For one-off events, use listenToOnce instead of listenTo
var _ = require("underscore"), Backbone = require("backbone"), EventAggregator, Listener; EA = _.extend({}, Backbone.Events); Listener = Backbone.Model.extend({ initialize: function () { var eventAggregator = this.get("eventAggregator"); this.listenToOnce(eventAggregator, "something:ready", this.onSomethingReady); }, onSomethingReady: function () { console.log("something ready from " + this.cid); } }); var instance = new Listener({eventAggregator: EA}); EA.trigger("something:ready"); instance = new Listener({eventAggregator: EA}); EA.trigger("something:ready");