New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Subscribe to deferred computed #855
Conversation
Thanks for bringing this up. Also thanks for writing a good clear spec! I'm not totally sure about the desired semantics of this scenario though. This proposed alteration would change the meaning of If you could describe the situation where you need to subscribe to a computed that is never naturally evaluated, that would be very helpful! Thanks. |
Hi Steven, Good point. I thought the purpose of It turns out thus far I've only encountered this when writing unit tests. But as our application gets more complex, I think it is likely to happen with a scenario sort of like this: function model() {
self.a = ko.observable(10);
self.b = ko.observable(20);
// we defer evaluation of this because it isn't always used and it is expensive to calculate
self.c = ko.computed({ read: someExpensiveCalculation, deferEvaluation: true });
}
function bindUpdatesToServer(someSubscribable, context) {
if (!ko.isSubscribable(someSubscribable)) { throw new Error ("must be subscribable"); }
return observableValue.subscribe(updateServer.bind(context));
}
// elsewhere in the application
var myModel = new model();
if (weNeedToSendCalculatedResultsToServer) {
// With current KO, this will not do anything unless "weNeedToVisualizeCalculation" is also true
bindUpdatesToServer(myModel.c, ...);
}
// in some view
<div data-bind="if: weNeedToVisualizeCalculation"><div data-bind="text: c"></div></div> This is a somewhat contrived example, but hopefully indicates the scenario. Only the creator of the computed should know/care that it is deferred. A consumer of the subscribable interface (like bindUpdatesToServer) doesn't even care if the object is an observable or computed. It wouldn't be appropriate for the bindUpdatesToServer() to do a To your original question. Instead of my current pull request (which is indeed potentially a breaking change), then what about either a new property, or (probably preferable) a special deferEvaluation value to signal this specific behavior. Something like: ko.deferUntilRead = true;
ko.deferUntilReadOrSubscribe = {}; // will test using === so will never match any existing code.
c = ko.computed({ read: ..., deferEvaluation: ko.deferUntilReadOrSubscribe }); // triggers new behavior. Guaranteed not to conflict with any existing code.
d = ko.computed({ read: ..., deferEvaluation: ko.deferUntilRead }); // existing behavior
e = ko.computed({ read: ..., deferEvaluation: true }); // existing behavior
f = ko.computed({ read: ..., deferEvaluation: anyTruthyValue }); // existing behavior (I cannot remember if you do an === true or just any truthy value. If you like the direction this is taking, then I'll update my pull request, and also include a spec which guarantees the existing Brandon From: Steven Sanderson notifications@github.com Thanks for bringing this up. Also thanks for writing a good clear spec! |
In my development with Knockout, I almost always use the
In my opinion, the current behavior is broken and needs to be fixed. Of course, we need to be careful when changing behavior. I'd like to hear what @rniemeyer thinks about this also. |
This is an interesting case. I feel that someone doing a manual subscription should not need to know if the computed is using However, I would not expect the subscription callback to be immediately called, if it triggered the computed evaluation. Like @mbest, I always manually trigger it, if that is required. If we are concerned about this being a breaking change, then we could consider providing a separate option to force evaluation on subscriptions, at least until the next major version. |
Yes I agree and I believe that is how I originally wrote this pull request. My intent was to make deferred computeds behave exactly the same as regular computeds (and observables) to an outside observer. As for backcompat, one could just define a constant to set deferEvaluation to for the new behavior such as... ko.deferUntilAccessed = {}; Now code that wants the new behavior just sets deferEvaluation: ko.deferUntilAccessed |
I wouldn't like So i think it is totally fine like it is now. But I admit that it is tricky to write lazy code. We try to setup everything so that most of the computeds are only evaluated and ran when a template finally accesses them. @rniemeyer What do you mean by "they could just peek?" - a peek to a computed also evaluates it! |
@lcorneliussen - What I said about peek was to clarify that I think |
Updated implementation is in #1272. |
Fixed with #1272 |
I'm misused the "bug" to "attach" functionality to a computed that I only want to execute, if someone else is/was interested in the values of the computed. How can I still achieve that behavior? An example: If someone is interested in an observableArray (by evaluating it...), I start listening to changes and call cleanup-functionality on discarded values. |
Hi,
If you create a computed with "deferEvaluation: true", and then you never read the computed, but you subscribe to it, your subscription will never trigger, even if you update the underlying observables. This pull request resolves that:
Example:
This is a trivial example, but imagine large applications where one piece of code creates the deferEvaluation computed (because the computation is expensive and not always used), and some other part of the application receives a computed that it should subscribe to. It doesn't know if the computed is deferred or not.