简体   繁体   中英

Array mutation without triggering a specific observer

I'm using web-sockets to transmit changes of a property, boardBucket to other clients.

  • boardBucket is a shared property of type Object watched by an observer.

  • When the observer is notified of changes, the changes are sent via sockets to other clients where they are applied to their own boardBucket .

The question now becomes how to prevent some object changes (via the Array mutation methods this.set() , this.push() etc.) from re-triggering that particular observer that sends the changes.

Without some-kind of silencing feature, the whole setup goes into an infinite round-trip between clients (one client changes boardBucket , other client applies the changes but that also triggers it's own boardBucket observer and resends them back on and on).


Is it possible to change the value of a property(or any of it's sub-items) without triggering a specific observer in some way?

    properties: {
      boardBucket: {
        type: Object,
        value: null,
        notify: true
      }
    },

    observers: [
      "_boardBucketChanged(boardBucket.*)"
    ],

    // if my `boardBucket` changes send changes to other clients
    _boardBucketChanged: function(changes) {
      sockets.send(changes);
    },

    // get changes sent by other clients and apply them
    _boardBucketReceived: function(changes) {

      // How can I silently make the following changes 
      // so it doesn't trigger **only** the above observer?
      this.set("boardBucket", changes); 
    }

Ideally, a flag could be passed when mutating an Object/Array which can be picked up in the observers like so:

    // flag can be read in observer
    _boardBucketChanged: function(changes, flag) {
      if(flag === "preventNotify") return false;
      sockets.send(changes);
    },

    // `flag` parameter can be passed with each mutation
    _boardBucketReceived: function(changes) {
      if(flag === "preventNotify") 
        return false;
      this.set("boardBucket", changes, "preventNotify"); 
    }

Note :

  • I could also use <iron-signals> (ie events) instead of observers but I'd prefer to avoid them, hence the question.
  • boardBucket is a property shared by multiple internal components with each component mutating and observing this. That's the reason I'm asking if only a specific observer can be skipped when mutating.
  • I could do some equality check between incoming/outgoing changes and if the path/value of changes is the same then ignore it and not send it. However I'd like to have a way to prevent some other mutations from reaching the socket-send functions hence this won't work. It's also a tad hacky.

I'd be interested to know what happens if you do something like this:

properties: {
  boardBucket: {
    type: Object,
    value: null,
    notify: true
  },
  settingBucket: {
    type: Boolean,
    value: false
  }
},

observers: [
  "_boardBucketChanged(boardBucket.*)"
],

// if my `boardBucket` changes send changes to other clients
_boardBucketChanged: function(changes) {
  //If we're setting, this is our change.
  //TODO: Is there any way that we can receive a change while
  //setting that is NOT the result of calling this.set? For
  //example if changes are batched and sent at once?
  if (!this.settingBucket) sockets.send(changes);
},

// get changes sent by other clients and apply them
_boardBucketReceived: function(changes) {
  //We are currently setting bucket, so ignore received changes
  this.settingBucket = true;
  this.set("boardBucket", changes); 
  //Now we're done
  this.settingBucket = false;
}

This also seems a little hacky, my understanding of the limitations of this approach are as follows:

If (and only if) this.set delivers exactly the changes passed to it before returning, then this should work. If it delivers any more changes, or delivers the changes it is passed asynchronously, then this won't work. Looking at the Polymer docs for Change notification protocol we have:

"When the property changes, the element fires a non-bubbling DOM event to indicate those changes to interested hosts."

Given a DOM event is used, I think it should be delivered before the this.set call returns.

So I think the only mechanism for this going wrong would be if anything listening to the boardbucket decided to make changes in response to your change - I guess you should be in control of this for your app, and it seems like it would be a bad thing to have in any case. If this happened, I can't see that any response to it is valid; either you ignore these "changes in response to changes" and boardbucket becomes desynced between clients, or you send them through the socket and you are back to a potential cycle.

In more detail on this, if we refer to changes in response to changes as changes^2 then the code just prevents changes^2 being sent to the socket. If the changes^2 are meaningless to other clients then this is the correct behaviour. If the changes^2 are meaningful to other clients then they would need to be sent to the socket I guess, but this would be inefficient since it uses a loop through the network to update the data in a way that is presumably NOT in response to user input. I would guess such changes^2 would be better made by the client that sees the original changes - it could work out what changes^2 were needed in response to the existing changes it is about to send, and add them in to send through all at once. That way only one network transfer is needed, and any receiving clients know the data is valid and will not trigger any changes^2.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM