Search code examples
javascriptecmascript-harmonyobject.observe

What does notifier.performChange actually do?


I am trying to understand Object.getNotifier(object).performChange. Conceptually I understand that it is designed for defining "macro" or higher level changes. From the example everyone seems to refer to:

increment: function(amount) {
  var notifier = Object.getNotifier(this);

  notifier.performChange(Thingy.INCREMENT, function() {
    this.a += amount;
    this.b += amount;
  }, this);

  notifier.notify({
    object: this,
    type: Thingy.INCREMENT,
    incremented: amount
  });
}

What I do not understand is, how is this different from simply executing the anonymous function passed to notifier.performChange directly, instead of as a callback? In other words, how does it differ from the below:

increment: function(amount) {
  var notifier = Object.getNotifier(this);

  this.a += amount;
  this.b += amount;

  notifier.notify({
    object: this,
    type: Thingy.INCREMENT,
    incremented: amount
  });
}

I have seen that in the latest spec, notifier.performChange may return an object, which is then issued as a notification, as in:

notifier.performChange(Thing.INCREMENT, function() {
    this.a += amount;
    this.b += amount;

    // a notification is issues with this return value,
    // including the type passed to notifier.performChange,
    // and the object underlying notifier. 
    return {incremented: amount};  
});

That eliminates the need for the following notifier.notify in the original code, but still, is this something other than sugar, or is there a functional difference between this and just making the changes and issuing the notification yourself?


Solution

  • After an hour of doing a lot of testing I finally figured it out. I had the same question (what is performChange for?), and also the same idea to just take that off and call

    this.a += amount;
    this.b += amount;
    

    However: the point of notifier.performChange is so that the observer does not observe each change.

    I was testing it like this:

    var obj = {
      x: 5,
      y: 10
    };
    
    function noti() {
      console.log('noti start');
      var notifier = Object.getNotifier(obj);
    
      notifier.performChange('ok', function() {
        obj.x++;
        obj.y++;
      });
    
      notifier.notify({
        type: 'ok',
        oldValue: 5
      });
      console.log('noti end');
    };
    
    function noti2() {
      console.log('noti2 start');
      var notifier = Object.getNotifier(obj);
    
      obj.x++;
      obj.y++;
    
      notifier.notify({
        type: 'ok',
        oldValue: 5
      });
      console.log('noti2 end');
    };
    
    function observer(changes) {
      for (var change of changes) {
        console.log('observer: change =', change, ' newValue=', change.object[change.name]);
      }
    };
    
    Object.observe(obj, observer, ['ok', 'update']);
    
    console.log('calling noti2()');
    noti2(); //will log the changes of update twice becuase of the x and y property of obj
    
    // add delay manually because observer calls are asynchronous and
    // we want to clearly separate the notification function calls in our logs
    setTimeout(function() {
      console.log('calling noti()');
    
      noti(); //will only log the ok type. that's what they mean by big change
              //so everything you do inside the performChange won't be observed
    }, 100);
    

    It should return the following console output:

    calling noti2()
    noti2 start
    noti2 end
    observer: change = Object {type: "update", object: Object, name: "x", oldValue: 5}  newValue= 6
    observer: change = Object {type: "update", object: Object, name: "y", oldValue: 10}  newValue= 11
    observer: change = Object {object: Object, type: "ok", oldValue: 5}  newValue= undefined
    
    calling noti()
    noti start
    noti end
    observer: change = Object {object: Object, type: "ok", oldValue: 5}  newValue= undefined