Destroying Custom Dojo Widgets

On March 13, 2014, in Javascript, by Anuj Gakhar

One of the things I learned today is that custom Dojo widgets do not completely get cleaned up if they have child widgets that have been programatically created. _WidgetBase’s internal call to destroy() or destroyRecursive() won’t completely destroy the programatically instantiated widgets and might leave some memory leaks.

Consider this piece of code :-

define([
	...
], function (
	...
) {
	return declare('LeakDemo', [_WidgetBase], {
		startup: function () {
			var row;
			this.inherited(arguments);

			// create more widgets
			for (var i = 0; i < 5; i++) {
				row = new SummaryRow({someProperty: someValue});
				this.containerNode.appendChild(row.domNode);
			}

			// subsribe to a topic
			topic.subscribe('SOME_TOPIC_PUBLISHED_ELSEWHERE', this._onTopic);
		},
		
	})
})

When the above widget gets destroyed, it will most likely leave some memory leaks around because the reference to the topic subscription and the row widgets will still be around even after the original widget is destroyed. Depending on your use case, this kind of thing can cause some nasty bugs in your app and can be quite tricky to track down.

Anyway, it’s not hard to solve such issues. We need to basically keep a track of any topics we subscribe to and any widgets we create programatically. We don’t really need to worry about the declarative widgets within the widget’s template as those are taken care of by Dojo’s destroy routine. With that in mind, the above code would be re-written as below :-

define([
	...
], function (
	...
) {
	return declare('LeakDemo', [_WidgetBase], {
		_topics: [],
		_widgets: [],
		startup: function () {
			var row;
			this.inherited(arguments);

			// create more widgets
			for (var i = 0; i < 5; i++) {
				row = new SummaryRow({someProperty: someValue});
				this.containerNode.appendChild(row.domNode);
				this._widgets.push(row);
			}

			// subsribe to a topic
			var t = topic.subscribe('SOME_TOPIC_PUBLISHED_ELSEWHERE', this._onTopic);
			this._topics.push(t);
		},
		destroy: function () {
			// remove any topic handlers
			array.forEach(this._topics, function (t) {
				t.remove();
			})
			// desroy widgets
			array.forEach(this._widgets, function (w) {
				w.destroy();
			})
			
			this.inherited(arguments);
		}
		
	})
})

Now that we are removing the references to the topic subscriptions and our own widgets, there should be no memory leaks. Just something to keep in mind while working in the Dojo land 🙂

UPDATE:
In Dojo 1.8+, Dijits based on dijit/_WidgetBase have an own method for “owning” event handlers. Owned handlers are un-registered when the widget is disposed of by calling its destroy method. So, the above code would be re-written as follows :-

define([
	...
], function (
	...
) {
	return declare('LeakDemo', [_WidgetBase], {
		startup: function () {
			var row;
			this.inherited(arguments);

			// create more widgets
			for (var i = 0; i < 5; i++) {
				row = new SummaryRow({someProperty: someValue});
				this.containerNode.appendChild(row.domNode);
				this.own(row);
			}

			// subsribe to a topic
			var t = topic.subscribe('SOME_TOPIC_PUBLISHED_ELSEWHERE', this._onTopic);
			this.own(t);
		}
	})
})

More details can be found here.

Tagged with:  

3 Responses to Destroying Custom Dojo Widgets

  1. Good stuff. I’ll be diving into dojo soon with ESRI JavaScript Web maps.

  2. You just saved my life! Thanks!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Subscribe to Blog via Email

Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Join 446 other subscribers

© 2011 Anuj Gakhar
%d bloggers like this: