I'm having a problem debugging my web applications. It's very frustrating because I've been trying to reproduce the problem with a small web page that I can post on jsfiddle, but this seems to be a case of the "Higgs-Bugson".
I have a web page with a large jQuery(document).ready()
handler. The problem is that when an exception is thrown from within the jQuery(document).ready()
handler, I get a call-stack with several anonymous functions and no pointer to the code that actually threw the exception.
Whenever I try to reproduce this behavior with a small web page, I always get a pointer to the code that threw the exception, but in production code, I never get the stack pointer. This makes debugging more frustrating and error-prone.
Does anyone here any any idea what could be causing this behavior and how to make it right?
Update: It's been several months since I posted this question and I now believe that I have conclusively reproduced the issue. I reproduce the issue with the following HTML:
<html xmlns="http://www.w3.org/1999/xhtml" >
<body>
Factorial Page
<input type='button' value='Factorial' id='btnFactorial' />
<script src="Scripts/jquery-1.6.1.js" type="text/javascript"></script>
<script type='text/javascript'>
$(document).ready(documentReady);
function documentReady() {
$('#btnFactorial').click(btnFactorial_click);
factorial(-1);
}
function btnFactorial_click() {
var x;
x = prompt('Enter a number to compute factorial for.', '');
alert(x + '! = ' + factorial(x));
}
function factorial(x) {
if (x < 0)
throw new Error('factorial function doesn\'t support negative numbers');
else if (x === 0 || x === 1)
return 1;
else
return factorial(x - 1) * x;
}
</script>
</body>
</html>
Looking at the code, you'll see that when you click a button, the code alerts the factorial of a number that you specify. Enter a negative number and an error will be produced. In this case, Visual Studio will catch the error and highlight the line of code where the error occurred and display a call stack including factorial
and btnFactorial_click
.
This code also invokes factorial(-1)
from the $(document).ready handler. In this case, Visual Studio moves the highlight to the finally block in the following jQuery code (excerpted from jquery-1.6.1.js, beginning on line 987
// resolve with given context and args
resolveWith: function( context, args ) {
if ( !cancelled && !fired && !firing ) {
// make sure args are available (#8421)
args = args || [];
firing = 1;
try {
while( callbacks[ 0 ] ) {
callbacks.shift().apply( context, args );
}
}
finally {
fired = [ context, args ];
firing = 0;
}
}
return this;
},
Although the call stack is not displayed, Visual Studio displays a pop-up showing the text of the error.
What is the reason for this odd behavior and how can I design my application so that I can easily trap errors that originate from handlers such as $(document).ready?
I'm having a problem debugging my web applications
What type of debugger are you using? They tend to vary and the best one that I have found when developing is firefox's because of some very nice integrated tools they have.
Although it is nice to reproduce a problem, when it cannot be reproduced in a simple succinct example then debugging becomes more and more important. Setting breakpoints and going line by line is sometimes the only way instead of waiting for the error to occur during runtime and then tracing back.
There are several approaches outlined in this MSDN article which could be useful to helping solve your problem. I am sure you have tried some of them, but it is at least worth looking at - notably "Scenario 3: I am getting an error deep inside a set of helper methods, and I'm not sure where the error originated."
"How to Debug Your jQuery Code"
http://msdn.microsoft.com/en-us/magazine/ee819093.aspx
Edit in response to new content
jsfiddle of new content: http://jsfiddle.net/EJbsS/
When running the jsfiddle in chrome, the first run through factorial(-1) is called. An error appears. It can be accessed by clicking in the bottom right at the red x, or by inspecting element, navigating to the console tab, and then looking at the text area. The error properly reads "Uncaught Error: factorial function doesn't support negative numbers ", with a link to the exact line where throw new Error()
is used.
Perhaps I need a little more clarification, what is the issue that you are trying to resolve? It seems that chrome debugs this script just fine. Perhaps there are some issues with Visual Studio's debugger.
Moreover
Here is a reproduction of a working page that can be used from notepad saved as .html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>demo</title>
<script type='text/javascript' src='http://code.jquery.com/jquery-1.6.4.js'></script>
<script type='text/javascript'>//<![CDATA[
$(window).load(function(){
$(document).ready(documentReady);
function documentReady() {
$('#btnFactorial').click(btnFactorial_click);
factorial(-1);
}
function btnFactorial_click() {
var x;
x = prompt('Enter a number to compute factorial for.', '');
alert(x + '! = ' + factorial(x));
}
function factorial(x) {
if (x < 0)
throw new Error('factorial function doesn\'t support negative numbers');
else if (x === 0 || x === 1)
return 1;
else
return factorial(x - 1) * x;
}
});//]]>
</script>
</head>
<body>
Factorial Page
<input type='button' value='Factorial' id='btnFactorial' />
</body>
</html>
When running this page in google chrome, navigating to the sources tab, and setting a breakpoint at Line 26: if (x < 0)
the entire call stack is reproduced when the page is run, on the right hand side.
factorial (debugpage.html:26)
documentReady (debugpage.html:16)
jQuery.extend._Deferred.deferred.resolveWith (jquery-1.6.4.js:1016)
jQuery.extend._Deferred.deferred.done (jquery-1.6.4.js:1002)
jQuery.fn.jQuery.ready (jquery-1.6.4.js:282)
(anonymous function) (debugpage.html:11)
jQuery.event.handle (jquery-1.6.4.js:3001)
jQuery.event.add.elemData.handle.eventHandle (jquery-1.6.4.js:2635)
Even further
Hopefully this caveat will be the best yet. Looking a little into stack tracing, I found this: console.trace();
. It is very useful.
Insert it into the above code at line 26 like so:
if (x < 0)
throw new Error('factorial function doesn\'t support negative numbers');
else if (x === 0 || x === 1)
Becomes
if (x < 0){
console.trace();
throw new Error('factorial function doesn\'t support negative numbers');
}else if (x === 0 || x === 1)
Running it now, without a breakpoint, will produce the stack trace in the console when the error condition is met:
console.trace() debugpage.html:27
factorial debugpage.html:27
documentReady debugpage.html:16
jQuery.extend._Deferred.deferred.resolveWith jquery-1.6.4.js:1016
jQuery.extend._Deferred.deferred.done jquery-1.6.4.js:1002
jQuery.fn.jQuery.ready jquery-1.6.4.js:282
(anonymous function) debugpage.html:11
jQuery.event.handle jquery-1.6.4.js:3001
jQuery.event.add.elemData.handle.eventHandle
I would suggest creating an object which is instantiated within the jQuery(document).ready()
block. For example:
var oStart = function(){
var pageMethod = function(){ /*...*/ }
var pageMethodOther = function(){ /*...*/ }
/*...*/
var init = function(){
jQuery(document).ready(function(){
/* do your stuff here */
});
}
}
oStart.init();
This will help you separate your application logic and the jQuery only logic. You should have very little in your ready block. Only those things which are absolutely necessary after the DOM has loaded completely. Everything else should be able to be loaded into memory while the page is still rendering.
I tried your small example with the built-in IE debugger and it reported the exception message just fine, placing the cursor on the throw.
The same in Chrome.
I have jquery v1.6.2. Maybe worth an upgrade?
Then again, this smells like a threading issue. If it always happens in conjunction with a UI update (like the alert), it could be the VS debugger is looking at a UI server thread when it thinks it's in the js thread. In other words, a bug in the debugger. If this is the case, you should try a different debugger to see if the problem disappears.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With