-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Proposal: new computed variant that's "active" only when observed #1359
Conversation
Sounds reasonable. This would be a new option you pass when creating the computed, would it? BTW, what kind of scenarios would you expect this to be used in? I'm not totally sure under what circumstances a computed would have no subscribers - normally I see computeds used in one of these scenarios:
So I'm curious about the cases when this would be effective. |
@SteveSanderson I wonder if I might have a use case for this and I think it would greatly improve performance for me. All the same I'm wondering if I'm missing the point here as my understanding of the inner-workings of a computed observable is somewhat incomplete... I have an array of view models which represent a collection of database entries. I maintain this collection (and many other collections like it) in the background for use over my application. This means that many viewmodels are retained in memory and are not bound to the DOM (and have no subscribers), however data received from the server may cause their computed observables to re-evaluate. So to clarify for myself, is the point of this feature request to only immediately re-evaluate a computed when it has subscribers (e.g. the DOM or other subscribers)? If an inactive computed was later subscribed to would this put it back into an active state (for immediate evaluation)? Furthermore could/would this allow for an inactive chain of computed observables:
Apologies if I've completely missed the point @mbest was trying to make! |
@AdamWillden You are right on. In your example, |
@SteveSanderson As you mentioned, this feature would not be used for a computed observable that triggers side effects (equivalent to a manual subscription). But for most cases, this feature provides two benefits:
For most applications, I imagine that this could be made the default option for computed observables. |
Fantastic, I'm glad I understood correctly 👍 I had also wondered if it made sense for this to become to default behaviour. Are there any cases where this would break existing implementations? Either way I look forward to this being implemented :) I think you all do a brilliant job and I find your responses to be always patient and polite. Keep up the good work, you're making my job easier 😄 |
If this was made default, it would break:
I'd love this implemented too, but maybe save making it the default behaviour in a major version release and make it opt-in for 3.x (Speaking up as I have quite a lot of code implemented based on the async observable case :) ) |
Yep, in fact I do that at least a couple of places in my code and I can see how it might be used extensively in others! Thanks for the additional clarification. |
Thanks, @alvingonzales, for spelling that out. I am not planning to make this the default option for computed observables but merely suggesting that developers could make it the default setting for their own applications (for example, I use |
I've started to work on an implementation of this and would like some feedback on the API, specifically how things should be named. So far, I have a |
What are you asking? Do you mean that you don't like the term 'sleep'? If so, you could name the option |
@AdamWillden, I'm just looking for feedback. What's your opinion on the names? |
functionalComputed does't mean anything to me. Perhaps you could explain why you think it suits? I think restful or even lazy better describes it to emphasise how it only does work when it needs to. Lazy fits in with the sleep option ;-) I'm not overly fussed of course but I dont think functionalComputed separates it in behaviour - is the standard ko.computed not functional?! |
I mean "functional" as in functional programming; specifically relating to avoiding state and mutable data within the computed's function. |
Thanks @mbest. While I do think myself an experienced programmer, I probably wouldn't be considered seasoned, either way I'm far from new and even after skimming that WIKI I don't feel the name is memorable (in relation to it's perceivable behaviour) or succinct (dependantObservable -> computed). I understand it may be a fitting name with regards to its inner workings but this is an 'outward facing' name. I, like many newcomers, read the documentation (which is brilliant) and I believe names should make sense or at least be explainable to non-programmers. If it could be explained to someone who's not a programmer than it's likely to make more sense to someone who is and therefore be memorable making the framework more accessible. Would be nice if someone else reading this were to chime in with their thoughts however - cue fellow commenter Not to mention you can save me 6 characters of typing 😉 |
How does the implementation look? Is it a small addition to the existing ko.computed logic, or does it involve major refactoring? About naming, that's always a challenge :) If you want to emphasise the "functional" use case, which I do agree makes sense, then I guess a natural candidate term would be For side-effect-free evaluators, it's always preferable to suppress evaluation if the result isn't being used, and for side-effectful ones, it's (virtually) always necessary to trigger the side-effect regardless of whether the return value is being used by something else. This looks quite neat and natural to me:
|
Actually I recognise that the kind of purity implied is only about lack of side-effects, and not lack of dependence on external mutable state. The exact definition of "pure function" given on wikipedia wouldn't be applicable here, since computed evaluators never accept parameters. Instead we would regard "all other state in the system" as being an implicit parameter to the function, and by that definition a side-effect-free function does qualify as pure. Of course, this might confuse and offend people, so maybe it's not the best choice of name :) If we wanted to be really explicit,
But that is a lot of typing. |
Other possible, non-pejorative shorthands for "no side effects":
|
Steve, thanks for the suggestions. The implementation so far is pretty small. I'm still working on the tests and making adjustments. |
Pretty sure this is related to this issue I've just raised: #1390 I extend observables to give me events when they are subscribed to and unsubscribed to like this:
This lets me do things like getting data with ajax and subscribing and unsubscribing to signalR endpoint when observables are subscribed to and unsubscribed from. This is quite good because you just have to look at stuff from the view for data to get fetched from the server. however when I have observables extended like this and referenced by a computed which then gets subscribed to:
and I have some markup:
then when lookAtComp goes false comp stays subscribed to obs I was quite surprised by this behavior actually In the code appended to my issue 1390 above there is a hack I made to get round this. Hopefully what @mbest is proposing will do it properly cheers |
…o subscribers. Add ko.pureComputed to encapsulate that interface, emphasizing that this approach works best if the computed doesn't modify any external state. Update specs to use ko.computed instead of ko.dependentObservable.
I've uploaded the implementation to 1359-pure-computed |
Thank you very much - I can confirm that resolves my issue. I think I might always be using pure computeds from now on as this is how I had assumed they worked. |
I wonder also if pure computeds should default to deferEvaluation:true? |
A |
There are a couple of issues with this that I think should be changed.
|
…ort on initial evaluation for pure computeds.
Generally this looks great. Thanks for designing and implementing it! A few questions:
Also, I realised that my earlier statement Developers should use pureComputed if and only if the evaluator has no side effects is wrong. I think the truth is: Developers should use pureComputed if and only if both of the following hold:
We should be clear about this in docs. |
…ndencies; make copy of parameter object
Thanks for the feedback, Steve. I've made changes based on your suggestions. Here's what I was thinking:
|
Proposal: new computed variant that's "active" only when observed
Fantastic, thanks! The changes make sense and I think this feature will be really valuable. |
@mbest could I possibly ask you to make me v3.1.0 script with this added to it due to my lack of building competance many thanks |
It's available here: https://github.com/knockout/knockout/tree/v3.2.0-alpha/dist Or through Bower: |
Many thanks |
Currently, a computed observable, once activated, remains active until one of the following happen:
dispose
method.disposeWhenNodeIsRemoved
is removed or cleaned.disposeWhen
function returnstrue
.read
function returns without accessing any observables.To clarify, active means that the computed observable is subscribed to one or more other observables and is updated when any of those are changed. This is important to understand because it means that an active computed observable cannot be garbage collected until all references to both it and all of its dependencies are dropped in an application. This could result in a memory leak if, for example, a computed observable references observables outside its own view model.
I'm proposing a new computed observable mode in which it only maintains subscriptions to its dependencies when it also has subscribers. When it has subscribers, it will act just like a "normal," active computed observable, But as soon as it has no subscribers, it will "dispose" its subscriptions and enter an inactive, "sleeping" state.
These are the states that a computed observable can currently be in (not counting intermediate states while it is updating):
deferEvaluation
option).throttle
orrateLimit
extenders).This feature will add a new computed observable state: