1

计算的读/写问题:“写”导致“读”执行。

我有读/写计算(或者也可以是纯计算)。它是可写的,因为它可以从 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 时 - 每次也执行“读取”部分。我看到这是因为写入更新读取 - 但如果我不希望这样怎么办?如何处理这种情况?

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

4

2 回答 2

2

避免在计算中计算中间值的最简单方法是使用延迟更新

使用延迟更新可确保计算的可观察对象和绑定仅在其依赖关系稳定后才更新。即使一个 observable 可能会经过多个中间值,也只会使用最新的值来更新其依赖关系。

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

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

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

time period write
time period read
于 2016-06-08T19:24:04.737 回答
1

您所描述的实际上不是问题。它是如何write工作的。

在该read方法中,敲除创建对您评估的任何可观察对象的订阅。它需要创建这些订阅,以便在它依赖的另一个值发生变化时重新评估自己的值。

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

在该write方法中,您正在设置确定read值的可观察对象。如果您设置 2 个计算依赖的 observable,您将触发 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>

编辑:从评论中我现在了解到,对于操作而言,不触发多次读取至关重要。我在评论中提出的解决方案:

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

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
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

于 2016-06-07T11:06:42.903 回答