Say I have a Layout
model with width
and height
properties. And Say I want to update a view when either of them changes, but the updating process in computational intensive and requires the value of both width
and height
. The computation process also updates the ratio
property of the model.
If I do
this.listenTo(this.model, 'change:width change:height', this.doLayout);
I will end up with two doLayout
calls in the worst case and both will do the same, wasting resources.
If I do
this.listenTo(this.model, 'change', function(model) {
if(model.hasChanged('width') || model.hasChanged('height')) {
this.doLayout();
}
});
On first sight it looks like I solved the problem of doing the doLayout
calculations twice. But the way Backbone.Model
works is that since doLayout
sets ratio
, I will end up with a second change
event. The changedAttributes
for the first event is {width: ..., height: ...}
and for the second one it's {width: ..., height: ..., ratio: ...}
. So yeah, doLayout
is executed twice again...
Any solution other than rewriting the set
method?
Edit: Note that I need a general purpose solution. Hard-coding the width/height/ratio special case is not acceptable. In reality I have many more computed properties which are updated based on others and the way Backbone handles the change
event does not work well in this situation.
The easiest thing to do, for me anyway, is to change the way you think about the events. For instance, in this case, it isn't that you want to only do it once, it is that you want to do something after both have finished.
The easiest way to do that here would be to debounce the event:
That way it will only run once, even if it is called a bunch of times synchronously.
Note: The exact timing behavior of
_.debounce(..., 1)
depends on the browser's treatment of very small timeouts. If immediate synchronous-like behavior is important, you could replace the usage of_.debounce
with a custom debouncer written usingsetImmediate
.