knockout writeable computed会导致不必要的'read'调用

时间:2016-06-07 09:22:20

标签: knockout.js computed-observable

计算读/写问题:'write'导致'​​read'执行。

我有读/写计算(或者也可以是pureComputed)。它是可写的,因为它可以从startDate和endDate计算。但是,它也可以直接设置,因此它是“可读的”并且基于值startDate和endDate被设置。这是我的模型的简化版本:

 self.startDate = ko.observable(...);
 self.endDate = ko.observable(...);
 self.selectedTimePeriod = ko.pureComputed({
            read: function () {                
                console.log('time period read');
                var startDate = self.startDate(),
                    endDate = self.endDate(),
                    timePeriod = Enums.TimePeriodTypeEnum.None;
                timePeriod = Constants.MTD;                
                return timePeriod;
            },
            write: function (timePeriodValue) {
                console.log('time period write');
                switch (timePeriodValue) {
                    case Constants.MTD:
                        self.startDate(...);
                        self.endDate(...);
                        break;
                }
            }
    });

    self.timePeriodChange = function () {
        self.selectedTimePeriod(Constants.MTD);
    }

用户点击UI触发器self.timePeriodChange功能。因此在控制台中我看到以下内容:

time period write
time period read
time period read

因此,当我改变startDate和endDate时,执行'write'部分 - 每次也执行'read'部分。我看到这是因为写入更新读取 - 但如果我不希望如此呢?如何应对这种情况?

建议是使用peek,但这会导致其他问题(可观察不会更新)。 这是我的小提琴手:https://jsfiddle.net/o5kacas3/(更改下拉列表,即使执行了写入部分,也不会在UI上计算并实际改变其值)。

2 个答案:

答案 0 :(得分:2)

避免计算计算中间值的最简单方法是使用deferred updates

  

使用延迟更新可确保计算的observable和绑定仅在其依赖项稳定后才会更新。即使observable可能经历多个中间值,也只使用最新值来更新其依赖项。

     

当应用于计算的observable时,延迟扩展器也将避免对计算函数的过度评估。使用延迟更新可确保对当前任务中的依赖项的任何更改序列仅触发对计算的observable的一次重新评估。

self.selectedTimePeriod = ko.pureComputed({
    read: function () { /*...*/ },
    write: function (timePeriodValue) { /*...*/ }
}).extend({ deferred: true });

使用此方法,您将看到以下内容:

time period write
time period read

答案 1 :(得分:1)

你所描述的并不是一个问题;它是write的工作方式。

read方法中,knockout会为您评估的任何可观察对象创建订阅。它需要创建这些订阅,以便每当其他值依赖于更改时,它就可以重新评估自己的值

如果您使用obs.peek()而不是obs(),则可以在不创建订阅的情况下使用值。这意味着您的计算机将来不会自动更新。

write方法中,您需要设置确定read值的观察值。如果你设置2个可观察的计算器依赖,你将触发2次读取。

为了说明:通过设置下面示例中的计算,并在更改之间登录,您将看到实际更改为" CB"简要地:



var reads = 0;
var a = ko.observable("A");
var b = ko.observable("B");

// Read 0 is triggered by the computed itself: 
// it wants to know its initial value
var c = ko.computed({
  read: function() {
    // This line creates subscriptions to both `a` and `b`
    var value = a() + b();
    console.log("READ " + reads++ + ": " + value);
    return value;
  },
  write: function(str) {
    console.log("WRITE");
    
    a(str[0]); // Triggers read 1, since a changed
    
    // Momentarily, the value is 'CB'
    
    b(str[1]); // Triggers read 2, since b changed
  }
});

c("CD");

<script src="http://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
&#13;
&#13;
&#13;

来自评论的

编辑我现在明白,操作不必触发多次读取是必不可少的。我在评论中提出的解决方案:

重写代码以绕过可写的observable:

&#13;
&#13;
var a = ko.observable("A");
var b = ko.observable("B");

var getInitialValue = function() {
  return a() + b();
};

var createNewValues = function(str) {
  a(str[0]);
  b(str[1]);
};


var c = ko.observable(getInitialValue());
c.subscribe(createNewValues);


console.log(c()); // AB
c("CD");
console.log(c()); // CD
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
&#13;
&#13;
&#13;