-
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
bug in version 2.1.0pre Feb 24, 2012 #387
Conversation
Would you be able to provide the stack trace for the exception? I know that Chrome provides this in the console. Just click the little triangle next to the error message. |
I'll try to get you more info, but the exception is rethrown and the stack trace stops at the place it is rethrown from, which happens to be jquery's .ajax onsuccess handler. Within that success handler, when I step through each line of code step by step, it happens right after I set the value of an observable that many things depend on. I tried to step into each function and step through but there is too much code, and large arrays of items that are processed many times, so I can't easily tell which data item or which bit of code is causing this. Is there any way I can tell which bindings, subscription or dependentobservables are being triggered by that observable value changing and/or which one of them is throwing the error? Thanks. |
Thanks for working on that. Can you try a couple more things. First, can you try running it with my fork of Knockout. It's based on 2.1.0pre, but includes some changes that might solve this problem. It's at https://github.com/mbest/knockout/downloads I have a suggestion on how to get the stack trace. Wrap the observable update in a
|
Good idea, thanks, here is the trace you requested:
You are also correct, using your new code fixes the issue, so looks like you already took care of this, thanks! But if there is there some internal ko variable or easy way to tell which bindings, subscription or dependentobservables are being triggered by an observable value changing I would appreciate it if you could tell me how to do this anyway. Normally the bugs are in my code, so a way to determine where errors come from more easily would be very helpful to me. |
P.S. What's new since 2.0 in this version other than bugfixes? Can I use the $index variable in foreach's now? Thanks |
It seems that you have a computed observable that triggers an update to itself. This causes it to be updated recursively. Left unchecked, this would normally cause a stack overflow. In your case, you must be limiting the recursion in some way. My update that you tested adds a check to specifically prevent recursion, and it seems that 2.0.0 at least handles your case gracefully. Does this sound like what's happening in your case? |
Not sure. This is the only computed observable on that page:
There are a lot of subscriptions and other things going on on that page so I can't be sure though. Any ideas how I can find out which one? |
I've created a simple example that seems to reproduce the problem: http://jsfiddle.net/mbest/nEQbY/ |
I don't see any updates in the computed function. It could be something that's happening in a subscription though, related to #341 |
The issue here is a subtle change introduced in commit bdf5c4d regarding nested evaluations and how subscriptions get disposed. The new mechanism, while more performant, can suffer from the I'd say there are two main choices for how we handle this, depending on whether or not we regard nested evaluations to be a legitimate scenario or not.
I haven't yet thought of any even remotely sane situation where it's desirable for a
With that definition, solution (2) makes most sense, and avoids various issues to do with infinite recursion. I appreciate this is rather obscure and awkward to make sense of, but does anyone have any thoughts on this? |
I am in favor of option 2. I can't see a legit need for the evaluator to recursively call itself that is not better handled in other ways. |
Steve, I favor option 2 as well. I already implemented this in my "smart binding" fork and have attached the code changes here. |
I don't need recursive evaluation either. But in very complex relationships it would be very helpful to be able to see what all the dependencies are somehow. The order in which the subscriptions are triggered can be important and is difficult for me to determine. Is there any easy way to tell? Thanks. |
Option 2 is my preferred scenario as well. If you have a complex set of dependencies, I don't think it's uncommon to have a situation where "A" references "B" while "B" references "A" (even if it's massively indirect). Having this automatically solved would be very nice. I do have to second @pilavdzic by saying that a way to annotate subscriptions would be a 'nice to have', though that may be best left to a plugin that wraps the original ko.subscribable. |
I'd like to add that we should have some caution before making this change as it can break some existing code. Here's an example that uses recursion and works in 2.0.0: http://jsfiddle.net/nEQbY/4/ Here's the same example using my Knockout fork that prevents recursion: http://jsfiddle.net/nEQbY/5/ Notice that in the first case, the binding gets updated to 1, but in the second case it stays 3. Obviously code like this can and probably should be changed so that recursion isn't necessary. Maybe we should include a warning log whenever we've blocked recursion (at least in the debug build). |
Excellent implementation - thanks very much, Michael! I take your point about the possible change of behavior. This change, however, does make 2.1.0 more consistent with 2.0.0 in the real-world cases we know about (pilavdzic's original scenario) so on balance it is probably less breaking than not making the change at all. Let's review this if anyone discovers any other real-world cases where it makes a difference. |
Thanks, Steve. That sounds like a good plan. |
That's interesting - I've changed all of my computeds to pure mainly because I assumed they always worked but now I get the "A 'pure' computed must not be called recursively" error - curious as to why we need an error when it's pure? and would it be sensible to make masking the error an option? I am using 3.2.0 alpha |
If you're getting that error, it must be because the computed isn't actually "pure", that is, your |
Yes I see what you mean. My issue however was around the disposal of subscriptions see here #1390 This is crucial to my application because I extend observables to fetch and subscribe and unsubscribe to data on subscription and disposal. I am not sure if I am concerned about whether the computed are 'pure' or not. I will try and get my head round this and write more later. |
Sorry to dig up an old issue.
Since then, knockout has added In knockout 3.4.2, if a dependency of a computed change during the evaluation of the computed, then an evaluation of the computed is not restarted - that is true, an evaluation is not immediately restarted. But if http://knockoutjs.com/documentation/computed-dependency-tracking.html says:
True - but knockout does "restart" the evaluation of the computed "later" if |
At least for prevent you can use inside the computed ko.ignoreDependencies(function(args){}) |
I tried upgrading to latest 2.1.0pre from 2.0.0 in my application and on a page with a lot of complex functionality, nested templates, bindings, etc, in certain cases depending on the data I get error:
Uncaught TypeError: Cannot call method 'dispose' of undefined
2.0.0 works reliably as expected.
My specific case is very complicated to reproduce so I am hoping that it is still helpful just to let you know about this, in case you already have an idea what this might be?
Thanks!