【问题标题】:Array mutation without triggering a specific observer不触发特定观察者的数组突变
【发布时间】:2016-03-27 12:48:32
【问题描述】:

我正在使用网络套接字将属性的更改 boardBucket 传输给其他客户端。

  • boardBucketObject 类型的共享属性,由观察者监视。

  • 当观察者收到更改通知时,更改会通过套接字发送到其他客户端,并应用到它们自己的boardBucket

现在的问题变成了如何防止某些对象更改(通过Array mutation methodsthis.set()this.push() 等)重新触发发送更改的特定观察者。 p>

如果没有某种 silencing 功能,整个设置进入客户端之间的无限往返(一个客户端更改 boardBucket,另一个客户端应用更改,但也会触发它自己的 @ 987654329@观察者并不断重新发送它们。


是否可以在不以某种方式触发特定观察者的情况下更改属性(或其任何子项)的值

    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); 
    }

理想情况下,当改变对象/数组时可以传递一个标志,可以像这样在观察者中拾取:

    // 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"); 
    }

注意

  • 我也可以使用<iron-signals>(即事件)而不是观察者,但我更愿意避免使用它们,因此提出了这个问题。
  • boardBucket 是多个内部组件共享的属性,每个组件都会发生变异并观察到这一点。这就是我问在变异时是否只能跳过特定观察者的原因。
  • 我可以在传入/传出更改之间进行一些相等性检查,如果changes 的路径/值相同,则忽略它而不发送它。但是我想有一种方法来防止其他一些突变到达套接字发送函数,因此这不起作用。这也有点老套。

【问题讨论】:

    标签: javascript polymer polymer-1.0


    【解决方案1】:

    我很想知道如果你这样做会发生什么:

    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;
    }
    

    这似乎也有点hacky,我对这种方法的局限性的理解如下:

    如果(且仅当)this.set 在返回之前准确传递传递给它的更改,那么这应该有效。如果它提供更多更改,或者提供异步传递的更改,那么这将不起作用。 查看Polymer docs for Change notification protocol 我们有:

    “当属性发生变化时,元素会触发一个非冒泡的 DOM 事件,以向感兴趣的主机指示这些变化。”

    鉴于使用了 DOM 事件,我认为它应该在 this.set 调用返回之前交付。

    因此,我认为出现此问题的唯一机制是,如果任何监听 boardbucket 的内容决定做出更改以响应您的更改 - 我想您应该为您的应用控制这一点,而且看起来它会无论如何都是一件坏事。如果发生这种情况,我看不出对它的任何回应是有效的;要么您忽略这些“响应更改的更改”,并且 boardbucket 在客户端之间变得不同步,要么您通过套接字发送它们,然后您又回到了一个潜在的循环。

    更详细地说,如果我们将响应更改的更改称为更改^2,那么代码只会阻止更改^2 被发送到套接字。如果更改^2 对其他客户端毫无意义,那么这是正确的行为。如果更改^2 对其他客户端有意义,那么我猜它们将需要发送到套接字,但这将是低效的,因为它使用网络循环来更新数据,这种方式可能不是响应用户输入。我猜这样的更改^2最好由看到原始更改的客户端进行 - 它可以计算出需要哪些更改^2来响应它即将发送的现有更改,并将它们添加到发送所有立刻。这样只需要一次网络传输,任何接收客户端都知道数据是有效的,不会触发任何更改^2。

    【讨论】:

    • 很好:) 所以这实际上归结为一个互锁。但是如果 new 更改通过 while 设置完成 - 遥远的边缘情况,但仍然可能。真正的问题是您已经指出的问题 - 此机制将禁止响应 this.set() 进行任何类型的更改,因此它有点不灵活。
    • 关于设置发生时发生的新更改的问题,我认为应该没问题,只要不能以某种方式递归调用 _boardBucketReceived(响应 this.set),这将很奇怪。假设它只是来自 websocket 的回调,它一次只能调用一次。
    猜你喜欢
    • 2021-09-23
    • 2021-07-30
    • 2020-03-23
    • 2013-12-21
    • 2015-09-05
    • 1970-01-01
    • 1970-01-01
    • 2012-05-20
    • 1970-01-01
    相关资源
    最近更新 更多