Knockout: Force reevaluate of computed observable.

Created on 28 Jun 2013  路  18Comments  路  Source: knockout/knockout

I want to force computed observable to reevaluate as if it's dependencies changed.

few close nice-to-have feature

Most helpful comment

I would love a method to cause computed (and observable for that matter) to tell it's subscribers as if the value had changed

For non-computed observables, you can use observable.valueHasMutated(). For any observable, you can use observable.notifySubscribers(observable()).

All 18 comments

I keep coming up against this. At the moment it has to be done with a dummy observable, which is confusing and wasteful.

@SystemParadox - can you describe your scenario a bit more? Does your computed perform other functionality or are there non-observable things that change that make it generate different output?

In my case there were a deep dependency tree of computed observables there were performing some actions in addition to returning result, I wanted to force reevaluate of tree to trigger these actions. I guess I could just make a graph of functions by hands...but you dependency tracking code is really nice )

My main case at the moment is a computed which makes use of non-observable data based on observable parameters. The computed needs to re-run if the parameters change, but I also need to be able to force it to update periodically on a timer, in addition to manually invalidating it.

The data is large and used by other computed functions. If the entire data was a single observable, it would cause too many expensive and unnecessary updates at once. Manually invalidating the simple cases keeps things feeling responsive, with the timer update catching the more complicated changes.

This isn't the only case. It would be useful for computed-action systems too.

I don't know if you would want to just expose evaluateImmediate or make a new method (invalidate? refresh?).

+1 for this, I use the standalone observable and push Math.Random into it when I want to force certain computeds to reevaluate. It's really handy when you're also using the datasource extender to fetch remote data.
I would love a method to cause computed (and observable for that matter) to tell it's subscribers as if the value had changed

I would love a method to cause computed (and observable for that matter) to tell it's subscribers as if the value had changed

For non-computed observables, you can use observable.valueHasMutated(). For any observable, you can use observable.notifySubscribers(observable()).

valueHasMutated() fail to me, using 2.3.0.

  • vm.myobsv.valueHasMutated(); or vm.myobsv.notifySubscribers(vm.myobsv());

so I continue with work-around like others said

  • vm.myobsv(-1); followed by vm.myobsv(0); to get the notifications.

valueHasMutated and notifySubscribers have nothing to do with this topic. Those are for telling subscribers that a value has changed. This thread is about making the entire computed function re-evaluate, including re-executing the function itself:

var x = ko.observable(2);
var y = ko.observable(3);
var z = 4;
var c = ko.computed(function () {
    function_with_side_effects();
    return x() + y() + z;
});
console.log(c()); // 9
z = 5;
c.refresh();
console.log(c()); // 10

I stumbled across this issue in trying to figure out how to effectively test a computed observable.

In the following two fiddles, I'm testing a small (and contrived) example using Jasmine.

This one is with a default 2.3.0 build that, from what I can tell, has no way to force a manual evaluation of a ko.computed, so the spec fails.

The second one uses a custom knockout build that exposes evaluateImmediate on the dependentObservable returned from ko.computed.

The second one is a super small change to the dependentObservable.js (one line, specifically), and something like this would allow for unit testing a computed observable in isolation. I'm not very familiar with the knockout codebase, so I'm not 100% sold on the idea of exposing this function due to what other effects it might have, but this was a quick fix to ease my testing woes. Thoughts?

@jtrim, I really like this. A common situation I find myself in is needing to recalculate computed observables on an interval. I could have an observable that's updated on an interval by another function, but I like to be able to go to self.timeSinceUpdate and see how it's calculated.

So, instead of this:

self.timeSinceUpdate = ko.computed(0)

// can't just pass it to setInterval, because it needs to be called right now, like a computed
var updateTimeSinceUpdate = function(){ self.timeSinceUpdate(1234) }
setInterval(updateTimeSinceUpdate , 1000)
updateTimeSinceUpdate()

we could do this:

var evaluate = ko.computed.evaluateImmediate

self.timeSinceUpdate = ko.computed(function(){ return 123})
setInterval(evaluate.bind(self.timeSinceUpdate), 1000)

In this case there aren't any observables referenced from the computed, but it's still data that needs to be rendered to HTML.

This can be accomplished with a custom wrapper function:

ko.forcibleComputed = function(readFunc, context, options) {
    var trigger = ko.observable().extend({notify:'always'}),
        target = ko.computed(function() {
            trigger();
            return readFunc.call(context);
        }, null, options);
    target.evaluateImmediate = function() {
        trigger.valueHasMutated();
    };
    return target;
};

Updated @jtrim's example: http://jsfiddle.net/mbest/4vNcb/

@mbest Good call. It's arguable that this is a core concern of knockout anyway, so I think this is an acceptable solution for my use. Thanks!

@mbest, the wrapper function is what everyone currently has to use and I don't consider it to be a long-term solution. It seems rather inefficient for everyone to reimplement this themselves and force an additional observable to be used on every computed.

All that is required is for evaluateImmediate to be exposed. It seems perfectly reasonable for knockout to have a "re-evaluate now" method added to the API.

@mbest : thanks, It solved my problem....

:+1: for allowing an API method on computeds that forces re-evaluation. My use case is that I'm using a computed() for it's caching and subscription properties (much like a regular observable) with the exception that I'm using custom read/write functions to stash a value in a custom location of the model. (it could in fact be one of several locations depending on some custom logic)

Bottom line is that a write call to the computed does not trigger an evaluation and thus any subsequent reads of the computed return the stale _latestValue.

At least I could use a computed() option like forceEvalAfterWrite which would just set _needsEvaluation = true after a write call.

I'm trying to implement a pausing for notifications while mapping a json object, but stalled computeds are not allowing me to do. Here is the code: https://jsfiddle.net/wj11vx3w/23/

I expect that when I click "Map with pause" it would log "a2, b2", "a3, b3", etc, but it logs "a2, b1" as the "Just map".

When Knockout fires the "id" subscriptions, the "name" value if cached in latestValue and gets the old value. Can I get it updated?

If I use the underlingValue as in the commented code, I can then get the updated value.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

craxal picture craxal  路  7Comments

ricardobrandao picture ricardobrandao  路  8Comments

andersekdahl picture andersekdahl  路  6Comments

andrewchch picture andrewchch  路  3Comments

johnpapa picture johnpapa  路  9Comments