I have been experimenting with Object.observe in Chrome v36. My original intent was to use this for business logic in my models, but the asynchronous behaviour seems to make this impossible. I have boiled this down to the following example:
function Person(name) {
this.name = name;
this.someOtherProperty = null;
this.watch = function()
{
var self = this;
Object.observe(this, function(changes){
for(var i = 0; i < changes.length; i++)
{
if(changes[i].name == "name")
{
self.someOtherProperty = changes[i].newValue;
console.log("Property Changed");
}
}
});
}
}
$(function () {
var p = new Person("Alice");
p.watch();
p.name = "Bob";
$("#output").text("Output: "+ p.someOtherProperty);
console.log("Output");
});
JSFiddle link, with jQuery.
My issue is that "Output" is called before "Property Changed". Is there any way to make Object.Observe synchronous, or should I be doing this a better way? (I am using AngularJS, btw.)
The issue here is not adding text to the DOM, or outputting to the console. My business logic requires me to immediately update someOtherPropety
when name
changes, and I'd prefer encapsulating this logic in my model.
Obviously, this is just an example case, but I have business rules that rely on being executed instantly.
It doesn't make sense to have
Object.observe
behave synchronously. It would have to block the thread and wait until something changes.You should pass a callback to your
watch
function to be executed whenever something changes:BTW, if you are using Angular (which by your code and fiddle is not at all obvious) you shouldn't care about executing any code when an obseved change happens. As long as you wrapped the code in
$scope.$apply()
Angular would take care of updating the view etc.E.g.:
See, also, this short Angular demo.
Better yet, see this more "real-worldy" demo.
Basically, the advantage of using
O.o
instead of Angular's dirty checking is that you save on$$watchers
and thus your$digest
cycles are faster and less expensive.Angular will also use this mechanism (
O.o
) anyway when ES6 comes out.Object.observe
, "sadly" (read next), doesn't perform a synchronous task. It sends notifications of the changes as soon as a "micro-task" ends.This is explained here.
So, your "micro-task" ends after
console.log("Output")
has been called, thenObject.observe
notifies the changes on the object.The classic method to have synchronous events is using getters and setters instead:
Of course, that would force you to create getters and setters for every property you want to watch, and forget about events on deleting and adding new properties.
As you say, observe is not synchronous. But you could maybe make watch take a callback and do your update of "someOtherProperty" there. Like this
Updated jsfiddle