Object.Observe Synchronous Callback

可紊 提交于 2019-12-04 11:21:21

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.

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

$(function () {
    var p = new Person("Alice");
    p.watch(function(){
        $("#output").text("Output: "+ p.someOtherProperty);
    });
    p.name = "Bob";
    console.log("Output");
});

Updated jsfiddle

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:

this.watch = function (callback) {
    var self = this;
    Object.observe(this, function (changes) {
        changes.forEach(function (change) {
            if (change.name === 'name') {
                self.someOtherProperty = change.newValue;
                console.log("Property Changed");
                callback();
            }
        });
    });
}

$(function () {
    var p = new Person("Alice");
    p.watch(function () {
        // !!!!! OF COURCE YOU SHOULD NEVER DO IT LIKE THIS IN ANGULAR !!!! //
        $("#output").text("Output: " + p.someOtherProperty);
        console.log("Output");
    });
    p.name = "Bob";
});

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

<div ng-controller="someCtrl">
    Output: {{p.someOtherProperty}}
</div>

.controller('someCtrl', function ($scope, Person) {
    $scope.p = new Person('Alice');
    $scope.p.watch();
    $scope.p.name = 'Bob';
});

app.factory('Person', function ($rootScope) {
    return function Person(name) {
        var self = this;

        self.name = name;
        self.someOtherProperty = null;

        this.watch = function () {
            Object.observe(self, function (changes) {
                $rootScope.$apply(function () {
                    changes.forEach(function (change) {
                        console.log(change);
                        if (change.name === 'name') {
                            self.someOtherProperty = self.name;
                        }
                    });
                });
            });
        };
    };
});

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!