Debugging JavaScript: Throw Away Your Alerts!

One JavaScript statement that rarely surfaces in the wild is throw(). The throw statement allows you to throw a formal error — sending details of the error to the browser’s error console — and halting further execution of the script.

The throw statement is particularly useful for APIs developers, as a way to warn its user-developers about runtime issues, such as missing or invalid input data. Its use should be generally preferable to alert() because it makes best use of the browser’s native functionality (and it’s less annoying):

if(typeof input == 'undefined')
{
	throw('Missing input');
}
else if(parseFloat(input) != input)
{
	throw('Input must be a number');
}

Using throw() with a string argument will throw a general error. However you can also create and pass an Error object, with which you can specify more details about the error:

var err = new Error();
err.name = 'My API Input Error';
err.message = 'Input must be a number';
throw(err);

The example above produces this output in Opera’s error console:

Opera's error console showing the error just detailed

Firefox’s console doesn’t show the error name, only the message; but it’s still almost as useful:

Firefox's error console showing the error just detailed

The important thing to note is that anywhere you use throw() will halt the execution of the script (not just the enclosing scope). So you should arrange your code so that any throw() statement comes at the end of an execution block:

function err(type, message)
{
	var err = new Error();
	err.name = 'My API ' + type + ' Error';
	err.message = message;
	throw(err);
}

// the delete in this example won't happen
if(typeof this.input == 'undefined')
{
	err('Input', 'Missing input');
	delete this.input;
}

// but this delete will happen fine
if(typeof this.input == 'undefined')
{
	delete this.input;
	err('Input', 'Missing input');
}

Of course, a single language statement — however useful — can never replace a fully-featured debugging tool like Firebug; a proper debugger lets you set breakpoints, watchpoints, step-through execution … all kinds of goodness. Firebug even lets you log messages to its own console.

But Firebug is a browser-specific tool, where this is a cross-browser language feature, so it can be safely integrated into the heart of a script without any chance of unsatisfied dependencies.

Win an Annual Membership to Learnable,

SitePoint's Learning Platform

  • Klas

    Firebug Lite is cross-browser…

    http://getfirebug.com/lite.html

  • Phil

    This blog is so off the mark I don’t know where to start. Throw is not a debugging tool. Use it to delegate unrecoverable errors to further up the chain of execution, where it may be handled more gracefully with a try catch. You should never intend for your thrown error to reach the surface.

  • aj[fp]

    Phil Says:

    This blog is so off the mark I don’t know where to start. Throw is not a debugging tool. Use it to delegate unrecoverable errors to further up the chain of execution, where it may be handled more gracefully with a try catch. You should never intend for your thrown error to reach the surface.

    I second this… you are meant to handle the error with the try / catch, not just throw. Throw literally “throws” the exception string to catch, for the try / catch to handle gracefully.

  • active8

    Yeah. I’m better at C++ but since learning that JS (ima newb) allows try catch blocks this was the first thing to come to mind when I read this. Seems to me you throw the error message and print it in the catch block. Correct me if I’m wrong. Even C++ error handling seems to be a foggy memory.

  • Breton

    Here’s a much better alternative. Continue to use the alert function as normal, but overwrite the alert function with a simple console logger. I find this to be quite useful, as there’s no legitimate usage of alerts in applications other than to annoy your users. I find it satisfying to destroy it it outright. When you’re done debugging, just replace alert with a stub function that does nothing. (or even silently ajax the alerts to a serverside error logger!) Here’s a simple one I threw together JUST NOW. No warrantees, only tested in firefox 3, not garaunteed to work anywhere. But give it a try, you might like it:

    alert = (function () {
    var console = document.createElement("div");
    console.setAttribute("id", "console");
    console.setAttribute("style", "width:300px; height:300px; font-family: monospace; font-size: 10px; border: 1px solid black; background: white; position: absolute; right: 0; top: 0; overflow: scroll;");
    document.body.appendChild(console);

    return function (s) {
    var p = document.createElement("p")
    p.appendChild(document.createTextNode(s));
    console.appendChild(p);
    p.scrollIntoView();
    }
    })()

  • Breton

    Apologies for the “inline” code. I didn’t notice the code block button!

  • http://www.brothercake.com/ brothercake

    I never said that throw was useful as a debugging tool. I said it’s useful as a high-level caretaker for other people using something you wrote.

    I guess the post’s title is a bit off the mark.

  • naterkane

    As Klas was saying… Firebug Lite is cross browser, he didn’t mention that you don’t need to run it on your server if you use a Bookmarklet to call it up. Save the javascript url below as a bookmarklet, and enjoy firebug anywhere, on any site.

    Firebug Lite Bookmarklet

    The only downside to this approach, is that you have to manually load firebug and anything that’s running before it’s loaded is not available (console, net transfers, etc…) which doesn’t help you if you have a window load or dom load based app to test.

  • naterkane

    here’s the url for that bookmarklet since it looks like sitepoint’s comment system ate it.

    javascript:var%20firebug=document.createElement(‘script’);firebug.setAttribute(‘src’,’http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js’);document.body.appendChild(firebug);(function(){if(window.pi&&window.firebug){firebug.init();}else{setTimeout(arguments.callee);}})();void(firebug);

  • Red T-Rex

    The DOJO framework provides a simple cross browser mechanism that creates a console in browsers like IE that do not natively support it. Then you can use something like:

    console.debug(“Deleted record: ” + recordId);

  • nathj07

    The blog was useful in serving as a start for great discussion. so from that point of view – thanks; I’ve learnt some stuff here.

    As for my 2cents worth well, I use Firebug to debug any pre-release code I have and then once I know it’s working correctly there some cross platform testing sees me right. All this means that the released code is working fine. JS is supposed to be cross-browser.

    As for the earlier post from Breton that’s a great idea. We should never throw our error messages to users on the web. They are unliekly to report the problem and will simply get what they are after somewhere else. So if you must handle exceptions (my JS is a ll fairly simple) which is a good idea in more complex systems then a logging tool such as this is a great idea.

  • greg

    Instead of throw() function, I would rather use throw statement:
    throw {
    name: 'My API Input Error',
    message: 'Input must be a number'
    };

    Compare it with more clumsy throw() function:
    var err = new Error();
    err.name = 'My API Input Error';
    err.message = 'Input must be a number';
    throw(err);

  • hj

    Greg said:

    Instead of throw() function, I would rather use throw statement:
    throw { name: ‘My API Input Error’, message: ‘Input must be a number’ };
    Compare it with more clumsy throw() function:
    var err = new Error(); err.name = ‘My API Input Error'; err.message = ‘Input must be a number'; throw(err);

    Umm, you do realize that throw is a statement? Like return (which takes an optional argument), throw takes one argument, which is expected to be an object constructed by Error. So your assertion that throw {object} is simpler, is simply wrong — you’re saying that


    return 4;

    is simpler than


    var i = 2;
    return(i+i);

    You can say, if you’d like:


    throw new Error('My API Input Error','Input must be a number')

  • xero

    Not quite…

    Try the following code in IE, FF, Safari, and Opera and see what you get (roll your own print() of course).


    try {
    throw new Error(42, 'foobar');
    }
    catch(e) {
    print('e is: ' + e)
    print('e.number is: ' + (e.number & 0xFFFF));
    print('e.number is (raw): ' + e.number);
    print('e.description is: ' + e.description);
    print('e.name is: ' + e.name);
    print('e.message is: ' + e.message);
    }

    Cycle these variations in to see how that goes…


    throw 'foo';
    throw { name: 'foo', message: 'bar' }
    throw new Error(42, 'foobar');
    throw new Error('I detect an error!');
    throw new SyntaxError('Your syntax is no good');
    throw new Error('foo', 'bar');

    https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Error
    http://www.webreference.com/js/column118/6.html
    http://www.javascriptkit.com/javatutors/trycatch2.shtml
    http://msdn.microsoft.com/en-us/library/t9zk6eay.aspx

  • http://www.devanagarifonts.net Mohit

    Awesome tutorial. Thanks for it.