I recently wrote some code that was similar to this (although much more complex and deeply structured – but this gets the point across).
In this example, the FiltersController attaches to an event on the Postal.js channel every time a new controller is created.
Having global channels access via name is great. You can use this to get multiple parts of your app to communicate, without having to pass around broker instances. Just ask for the channel by name, and you have the same one everywhere you ask for it.
But adding a callback method to the global channel and never cleaning it up is dangerous. Once you add that callback, the global channel will hang on to it even if the FiltersController instance is garbage collected.
The global channel might seem ok off-hand… but the next time you create a new FiltersController instance, you add yet another callback as well. Trigger the “add” event and you will have 2 or more callbacks firing – some of which will be looking for objects that may not be available or may be in a state that cause a myriad of potential problems.
The result is a zombie object with strange errors like the one shown here. The worst part about these errors is that the code looks fine and runs fine the first time. It takes two or more instances of the object for the problem to show itself – something that often doesn’t happen until a long time later when it’s not obvious what the cause is.
The easy fix is to ensure you clean up your callbacks on the global channel. But that “easy” fix isn’t always easy – and it isn’t always the best way to fix the problem.
Local Broker Instance
There’s nothing wrong with global broker channels, if you remember to clean up your handlers. But I find that local broker instances can often be useful for situations where I know the limited scope of a given channel.
In the case of the FiltersController, I replaced my global channel with a local broker instance (using Backbone.wreqr in this case):
There are very few changes to this code, but the changes are important.
Now, when a new FilterController is created a local broker instance is created with it. This gives the broker a very limited scope. I can’t access this broker from any other code in any other part of the system. There is no global registry of broker channels – just this one broker instance.
That one change was enough to get rid of the zombies I was seeing.
When the FilterController was garbage collected, the broker went along with it. The next controller to be created used a new broker, preventing the zombies from occurring.
Know The Scope Of Your Broker
As I said before, there is nothing inherently wrong with global channels and channel-based brokers. I use Postal.js a lot and I really like it. The key is understanding the scope in which your brokers need to be available.
If you’re dealing with a very broad scope of events, commands and messages, then a global channel is probably a good way to go. If you have a very limited scope for a broker, and you need that broker to go away when the limited scope is shut down, use a local broker instance.
Keep your global channels global, and create local brokers for local needs.
Of course, nothing is that simple or cut and dry. If you do decide to use a global channel for your message broker, be sure to clean up the message handlers.