Computed stops triggering forever if dependency is inside of false branch statement

瘦欲@ 提交于 2020-01-01 16:46:08

问题


I'm faced a problem that my computed observable stops triggering after some sequence of dependency changes. Finally I found out the point: if dependency was inside of false branch statement during latest evaluation, computed will not be triggered next time even if condition became true before evaluation finished. Here is a sample: https://jsfiddle.net/sgs218w0/1/

var viewModel = new function(){
  var self = this;

  self.trigger = ko.observable(true);
  self.fire = function(){
    self.trigger(! self.trigger());
  };

  self.content = function(){
    var test = 3;
    return ko.computed(function(){
      alert("triggered!");
      if(test !== 0){
        console.log(self.trigger());
        alert(test);
      }
      test--;
    });
  }();
};

ko.applyBindings(viewModel);

Is it bug or feature? Do you know any workaround for this issue? I seems to be optimization, but it looks aggressive and incorrect for me. (Edit: I changed my mind. It is reasonable, but can lead to some issues sometimes. I think knockout should have options to fix this issues)

P.S. I could publish more detailed example of real code to make question more specific, if you need it. But the point of real code it the same.

UPDATE Well, I had to be less lazy to provide more detailed example of what I want achieve. I like the idea of computed which automatically make ajax calls. Described here. One disadventure I see is that call will be made even if corresponding part of UI is invisible. I tried to fix it this way: https://jsfiddle.net/bpr88bp3/1/. The problem is that once tab is deativated it can't be activated anymore, because computed stops triggering...


回答1:


After reading the update to your question and looking through the updated example code, I've come up with a real solution. This uses a pureComputed to do the update, taking advantage of the fact that a pure computed can be activated and deactivated by subscribing to it and disposing the subscription. Here is the important code:

updateComputed = ko.pureComputed(function () {
    updateTrigger();
    result(evaluator.call(owner));
});
ko.computed(function () {
    var isActive = result.active();
    if (isActive && !updateSubscription) {
        updateSubscription = updateComputed.subscribe(function () {}); 
    } else if (updateSubscription && !isActive) {
        updateSubscription.dispose();
        updateSubscription = undefined;
    }
});

https://jsfiddle.net/mbest/bpr88bp3/2/




回答2:


According to the Knockout JS documentation:

So, Knockout doesn’t just detect dependencies the first time the evaluator runs - it redetects them every time.

When if(test !== 0){ is false, Knockout does not subscribe to self.trigger() due to self.trigger() not being called during computed recalculation. With no subscription to the self.trigger(), there is no recalculation of the computed on further self.trigger() changes.

IMHO the workaround is to get self.trigger() in any case (updated fiddle):

self.content = function(){
    var test = 3;
    return ko.computed(function(){
      var triggerValue = self.trigger();
      alert("triggered!");
      if(test !== 0){
        console.log(triggerValue);
        alert(test);
      }
      test--;
    });
  }();



回答3:


The idea, in general, is that all dependencies of a computed observable should also be observables. In your case, test isn't an observable; if you make it observable, your code should work as expected:

self.content = function(){
  var test = ko.obseravble(3);
  return ko.computed(function(){
    alert("triggered!");
    if(test() !== 0){
      console.log(self.trigger());
      alert(test);
    }
    test(test()-1);
  });
}();

But this introduces another problem because you're now changing one of the dependencies within your computed observable. By changing test, logically, the computed should be evaluated recursively. If the computed is synchronous, Knockout prevents this, but this is not really a feature, and it could cause problems later.

Here's what I think is a better solution that properly isolates the meanings of the components:

self.content = function(){
  var respondToTriggerChange = ko.obseravble(3);
  self.trigger.subscribe(function () {
    respondToTriggerChange(respondToTriggerChange()-1);
  });
  return ko.computed(function(){
    alert("triggered!");
    if(respondToTriggerChange()){
      console.log(self.trigger());
      alert(test);
    }
  });
}();


来源:https://stackoverflow.com/questions/36036356/computed-stops-triggering-forever-if-dependency-is-inside-of-false-branch-statem

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!