[devlunch]

Six Things Your Mom Never Told You About Debugging Javascript

Posted by: andrewplummer on: July 29, 2009

One of the most exciting days we’ve had all year came a few weeks ago after a routine deploy, when someone opened up the site in IE to find that nothing worked. Such is Javascript. A simple syntax error in the code can, under the right circumstances, cascade down and break all other Javascript in its path. In our case this included the popup scripts that launch our apps.

A few of us spent a couple hours trying to track the problem down to no avail. It didn’t help that most of us don’t run Windows in the first place, but even after getting set up, the error messages are less than informative. In the end, it turned out to be one character that brought our whole site down:

error

So no problem right? Just look at the error message and correct the offending line. But tracking it down in the code proved to be devilishly difficult, and in the end caused our users hours of frustration. It all has to do with the way that IE chooses to handle Javascript errors. Fortunately, I took away a ton of good information, including some about how Javascript works in general. So, with perfect hindsight I offer it so that others may not be doomed to walk the same path:

1. Trailing commas in object literals (hashes) are syntax errors.

An example of this:

var hash = {
  foo: 'bar',
}

Is invalid in IE because of the trailing comma, while other browsers will allow it. Keep an eye out for these. Seriously.

2. Javascript Reserved Words Can’t be Used as Hash Keys

For example:

var hash = {
  new: 'bar'

}

The keyword “new” here will cause an error in IE. To prevent this, use strings instead for your hashes, like this:

var hash = {
  'new': 'bar'
}

3. Syntax errors prevent that block of code from being executed.

Javascript begins by evaluating a block of code for syntax errors. If it finds any, it will consider that block to be invalid, and skip to the next one. Here, each “block” is a <script> tag. This could be an external file, or in-page JS. It will move on to the next <script> block as if nothing has happened and NONE of the code will be executed, either before or after the error.

4. Other errors will cause only the remainder of the block to be skipped.

Once a block of code is evaluated and determined not to have syntax errors, it will immediately begin executing the code inside. If any errors occur during this process, execution will halt, and the remainder of that block will be skipped. However, any code already executed (variable and function definitions, etc) will remain.

5. In IE, The last error that occurred will be reported after all other code has executed.

Despite the fact that any errors (both syntax errors and otherwise) will fail and skip a given block of code, the errors themselves won’t be reported until the last block of code has been run. To get a better idea of how this works, let’s look at this flow a little more closely.

Let’s say you have three blocks of code. The first contains a syntax error, the second a different error, and the third is OK. IE will handle this code in the following way:

  1. First block evaluated.
  2. Syntax error is found. Error is noted and the block is skipped.
  3. Second block is evaluated.
  4. No syntax errors are found, so block is executed.
  5. Continues up to the error, which immediately halts execution and skips the block. Error is noted.
  6. Code has no errors, so it is immediately executed.
  7. Error noted in the SECOND block is now finally reported.

6. Error reporting is just dead wrong.

First of all, no matter where your script exists (external or inline), the error will always be reported as part of the page itself. This means that you won’t see “Line 3: script.js”, but instead “Line 3: page.html”, despite the fact that the error is in the JS file! Nevertheless, the line number given will be relative to the file itself, so “line 3″ will be the actual line 3 of the external file.

In addition, the line numbers themselves are messed up in ways I can’t even properly analyze. If I were to wager a guess, it seems almost as if the files were put through the parser, then newlines are “inferred” from where they should occur in context. And, as a final twist of irony, it seems that CRLF line breaks are evaluated twice in IE, despite being the Windows standard! That means that if you’ve been writing your HTML in a windows based editor, your Javascript error reported on line 20 is actually on line 10. Enjoy!

object_expected

So, now the stage is set for total disaster, and hours of wasted energy. Let’s watch how the game plays out, shall we?

IE begins evaluating the Javascript in my page. The first script blocks at the top contain libraries for use by the rest of the page, so it starts loading those first. It hits the first few scripts, and so far no problems. However, a few lines down, it hits some code of mine that contains a trailing comma in a hash, which triggers a syntax error. IE makes a note of the error, skips the block, and moves on. The next few blocks are error-free, so they get evaluated immediately. So far, as we expected. But wait! Now a script block later in the page calls a function contained in the block that had the syntax error. That block was skipped earlier, so now the function doesn’t exist! Now THAT block errors out, and the rest of the Javascript is skipped. Now, depending on how many different blocks call that same code throughout the page, other blocks may error, expecting the code that was inside where the original syntax error occurred. Or worse, other errors may occur expecting code inside the block where the SECOND error, caused by the first error, occurred, causing a chain reaction of biblical proportions! Our original error (remember it was just a single comma!), now has us stumbling down the entire page, only to come rolling out at the other end clutching in our hands something totally useless – the LAST error message that occurred.

Having fun yet? Now let’s step it up a notch.

In addition to this puzzling state of affairs, what REALLY threw me off was our asset packager. In production we bundle up the Javascript so that it is a single Javascript file, a common practice to limit http connections for the end user. However, when working in development mode, every Javascript file comes in separately as, you guessed it, a separate block. Ironically (or perhaps tragically), although my code, complete with it’s offending comma, was being included in the Javascript code base, it wasn’t being used anywhere throughout the site! This meant that on my development machine, where each Javascript file is separated, my code came in as a separate block, caused an error, but died peacefully on its own with no dependencies to worry about. However, on production where all the Javascript is bundled, it was taking out 90% of the code base with it, as that solitary syntax error caused the entire bundled package to be skipped, and the end result was a completely unintelligible error complaining that something didn’t exist that clearly should have.

At the time, I thought something screwy was going on with the asset packager, and began there. But every time I tried to reshuffle the code to try to pinpoint the error, a completely different error was thrown, as my little comma resulted in the wanton destruction of differently sized chunks of code that the rest of the site depended on. What’s more, every page would error out slightly differently, as different dependencies were being executed in different places. Nothing made sense anymore. Eventually the possibilities were narrowed down, and I found the comma and removed it, with absolutely no confidence that it would work. But it did.

facehugger

The Bottom Line

With my perfect 20-20 hindsight, I now realize that the problem boils down to two incorrect assumptions that were feeding off each other, one about Javascript itself, and the other about IE.

First, I was assuming that all Javascript is executed as a giant block. It’s not. Points 3 and 4 above are not IE specific. Each script tag is like its own environment, and an error in one block will not prevent scripts from being executed in the other.

Second, I was assuming that IE was reporting the FIRST error that occurred. This seems natural enough considering the first assumption – if a script errors, it’s now finished and what I’m left with is the point of failure. This is in fact NOT the case, however. IE is simply running through each block blindly, reporting errors as it goes along. Without an error console (which IE natively doesn’t have), all you get is the most recent error. Effectively, IE isn’t showing you the first (and hence most relevant) error that happens, it’s showing you the last.

The Solution

The first thing to remember is simply not to trust errors in IE. They’re not showing you what you want to see. However, you can MAKE them show you what you want by turning on debugging, which will raise every error as it happens. One of the best debuggers comes bundled with MS Office, and is talked about on quite a few other sites. Five minutes or so spent installing this could have saved me hours of wild goose chasing.

There are also ways to catch these kinds of errors outside IE, by running your code through JS Lint, or turning on “Strict Warnings” in Firebug, although you may get more information than you need.

In any case, although it’s painful, hopefully now being aware of WHY IE acts the way it does on errors will help deal with more emergencies in the future.

38 Responses to "Six Things Your Mom Never Told You About Debugging Javascript"

…and one thing your Mom did teach me about Javascript:
$(“body”).insert(“head”).slideUp().slideDown(); //etc etc

Best comment I’ve read all year. Hats off to you.

I see what you did there.

About that etc etc part… Would you use a while loop or a do loop for that? Or is it a foreloop?

Maybe this goes too far.

$(“head”).fireEvent();

Are you doing everything you can to check your code for errors before it breaks something? There are lots of good tools out there.

http://www.javaworld.com/javaworld/jw-05-2009/jw-05-javascript-tools-overview.html

Although it hurts my feelings a lot, I keeping running back to the open arms of JSLint, at http://jslint.org. Nothing EVER ships until it passes JSLint.

I agree. I use Textmate for my work, and it has the “Javascript Tools” bundle–which is a godsend. The bundle binds cmd+s to a quick JSLint validator that displays a tooltip with errors and warnings that will more or less alert you that you’re doing something wrong.

I believe other texteditors and IDEs have JSLint integration tools as well. No JS programmer should be left without JSLint. Seriously.

Tested with JSLint and it specifically notices the trailing comma

Step one with javascript testing: use JSLint

You released a website without testing it in one of the most widely used browsers?

Haven’t you got any integration tests using something like Selenium? http://seleniumhq.org/

We sure do. I also personally test all my code in all versions of IE before releasing it. However, as with all sites, this was the one that slipped through the cracks. There were a variety of coinciding factors at the time, mostly due to a hasty release as well the asset packager problem, mentioned above.

The asset packager wouldn’t be a problem if your integration tests were run against a packaged version of the site. Others have suggested using jslint which would also have caught this particular problem, packaged or not.

Not wanting to preach too much but you might want to do some root cause analysis of how you ended up releasing a site that didn’t work and take action to prevent it happening in the future (if you haven’t already).

Thanks, liked the article.

I try as much as possible to isolate my JS and write unit tests for it. Then its easy to test in all my target browsers and see exactly what works and what doesn’t (I catch the trailing “,” in object literals in my code all the time).

hoping TestSwarm (http://testswarm.com/) will eventually alleviate this pain of testing javascript on multiple browsers/versions.

Thanks for that. It explains at least some of what’s been giving me problems (particularly why the IE error messages seemed to be garbage). But please, *please* tell me what’s so good about the MS Script Debugger. I ran into *exactly* this problem when I had to get a site working with IE, and after looking around on the web, I found and installed the debugger. But it seems to be almost entirely useless. I can’t get it to give me a stack trace, or show the value of a variable, or anything else that I would expect a debugger to do. Is there some mystic way of getting it to tell me what IE doesn’t like about my JS? Because I haven’t found it so far…

Do you not have an environment to test on before you release? This should have been caught during the test cycle.

I use a binary search to locate errors in IE. Get the error, look at the line number, go to the middle of the js and put in a space. Check again. If the line number increases, the error is below that point, if it does not, it is above. Cut the result in half, repeat.

That is the way I debugged the first hanging comma problem I had.

First 3 points could be replaced with ‘write syntactically correct js’. Them’s the rules.

IE returns the line number of the error but this line number is relative to ALL script on the page, not the source file so is pretty unusable. A try catch(err) block interupts all processing as soon as the error happens, so might be a better way to find/deal with errors.

You’re confusing the point of the article, which is telling you HOW Javascript errors (the first two points being only an issue in IE, which is why they’re important) so you can catch them. Writing syntactically correct js is a given, and is something like saying “don’t make a mistake”.

I felt that IE was singled out as the pain. It complains about superfluous commas or using reserved keywords but other browsers don’t.

I loathe debugging on IE as much as the next person, but I would rather have something shit itself due to syntax errors rather than make assumptions about it.

As for the last error business, use window.onerror to decide what to do with an error

jslint is your friend. Use it without fail.

MS Script Debugger is quite helpful for showing you the trailing comma error you describe in point #1. As far as it helping with these other types of errors, well, that’s more hit-or-miss. Mostly more miss than hit, but still of some use.

Can’t tell you how many times jslint saved the day! Just can’t release the code unless it’s been through jslint. The other bug we found was after we pushed out the code that generated some dynamic jQuery divs just to find out that IE was a whole lot stricter than Safari and Mozilla. The offending code was $(”) while what ended up working consistently was $(”). jslint didn’t help in this case. :-(

Okay that should’ve been (less-than div greater-than) vs. (less-than div slash greater-than). *sigh* Double-escapes…Somebody should print a t-shirt for that one…

“Everything in Javascript is an object”, I see this quite often and I think it merrits correction, since not everything in JS is an object. It has literals, and performs boxing to make stuff look like objects. The distinction isn’t subtle, and it can bite you if you’re not clear on how this works.

foo = new String(“foo”)
foo.bar = “bar” // this works
baz = “barz”
baz.bar = “bar” // no error, but the property is “lost”, as soon as baz is unboxed.

Yeah in this case you ought to use the Express version of Visual Studio (free). Create a new web app and then use the newly opened instance of IE to browse to your testing site. All of your javascript code will load and you can easily set breakpoints as well as see (very quickly) where things went wrong.

Trailing commas gave me serious pain in the ass few years ago.

And yeah JSLint is the best option.

I did’nt understand about Java script

Another “binary search” approach involves splitting up your Javascript blocks with separate “try/catch” sections. Have your “catch” clause put up an alert with the text of the error plus some identifying words.

try {
// block of code
}
catch (x) { alert(“try block 1: ” + x); }

Fortunately, if you are using Netbeans it marks a trailing comma as an error, it will also warn you about things that won’t work in every browser.

Same is true with aptana if your turn on the jslint support.

JSLint helped me a lot to prevent such situations, it’s one of the most useful javascript develop tools i know.

Aptana+JSLint+Firebug totally rocks!

sometimes, I use netbeans to track nasty errors like trailing comma in my js code.

For 5),
IE reports the last errors, but it has Previous& Next buttons, we can use those buttons to navigate to errors!

Eheh this coma madness gave me my share of headaches in my beginings..

Forgetting a trailing semicolon in a minified JavaScript script can also be painful to debug.

But to me the ultimate worst IE abomination is the document.all in the window object.

This kind of problem is extremely hard to trace back and of course you can’t count on IE to give you any useful information whatsoever. So..

document.all.varName === window.varName;

Therefore IE6 Can’t distinguish JavaScript variable names from DOM element IDs and they can overwrite each others. This leads to nearly un-tracable bugs and meaningless errors message.

Nice info. heading into a class on Java now.

well I know your pain, but it’s possible to be better prepared for this kind of problems.
two essential tools for Internet Explorer testing are:
http://www.my-debugbar.com/wiki/CompanionJS/HomePage
http://www.my-debugbar.com/wiki/IETester/HomePage

companion.js console saved me a lot of time!

Leave a Reply