Search code examples
javascriptangularjsobject.observe

Object.Observe Synchronous Callback


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.


Solution

  • 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.

    Years of experience on the web platform have taught us that a synchronous approach is the first thing you try because its the easiest to wrap your head around. The problem is it creates a fundamentally dangerous processing model. If you're writing code and say, update the property of an object, you don't really want a situation having update the property of that object could have invited some arbitrary code to go do whatever it wanted. It's not ideal to have your assumptions invalidated as you're running through the middle of a function.

    So, your "micro-task" ends after console.log("Output") has been called, then Object.observe notifies the changes on the object.

    The classic method to have synchronous events is using getters and setters instead:

    Person.prototype.setName = function(name) {
        this.name = name;
        console.log("Name Changed");
    };
    
    p.setName("Bob");
    

    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.