Thursday, March 28, 2024

Javascript Basics Part 11

You have written a JavaScript application and it’s working fine–until you get an error message. It’s something unsightly that pops up on your screen, something like ‘myObject.fields is null or not an object’. What on earth does this mean? This article is going to show you how to account for errors and show you several different methods for general error handling.

Syntax Errors

JavaScript has two basic levels of error handling: syntax errors and runtime errors. Syntax errors occur before the JavaScript code even runs, basically meaning that the code can’t compile. Take the following code, for example:

for(var i=0; i<10; i++)
	// do stuff here
}

You’ll notice that we are missing the opening { character. If you try to run this code you will immediately get an error message, even if the code is in a function that is not executed immediately. These errors are almost always easy to find and fix. You will get a message saying something like “Expected ‘)’, line 10, char 18”. If you go to line 10 there will usually be a fairly obvious error, such as a missing ), an extra < or any other typo you may have entered. There’s nothing we can do about this type of error other than to just fix it and move on. Here is a list of some of the more common syntax errors:

  • Missing or Mis-Matched Braces, Parenthesis, or Brackets

    Every Brace, {, Parenthesis, ( or Bracket, [ must have its closing counterpart. If you have nested sets of these, the inner ones must be closed before the outer ones. For example, {[}] is invalid.

    Every if, for and while statement must also have its conditions surrounded by parenthesis. “if x=5{” is invalid because the “x=5” should have parenthesis around it. If you are having trouble with this, there are several editors out there, such as EditPlus that can highlight matching sets of braces, etc for you.

  • Missing or Mis-Matched Quotes

    This is a very common problem. Strings in Javascript begin or end with a ‘ or ” and must end with the same character. If that character exists in your string, then it must be escaped. For example, the code

    var x = 'It's a beautiful day';

    is invalid because the ‘ in It’s is not escaped. This code should read:

    var x = 'It's a beautiful day';
    
    // or
    
    var x = "It's a beautiful day";
    

    Another somewhat common mistake is to close a string with the wrong character, ie:

    var x = "It's a beautiful day';

    This string is started with a ” character, so it must end with a ” character.

  • Missing semi-colons

    Although semi-colons are not usually necessary in JavaScript, it is a very good practice to always use them. If you are trying to shrink your JavaScript file, for instance, a common practice is to remove all the line breaks. Take the following code:

    var x=5
    var y=10
    

    If we remove the line breaks we get

    var x=5 var y=10

    which breaks. If we had semi-colons in place this would not be an issue.

Runtime Errors

So we move on to runtime errors. Once you get your code to run at all, we start to encounter runtime errors. Runtime errors can occur for many, many reasons. Each of the following blocks of code will throw an error:

alert(x); // 'x' is undefined


var x;
x[5] = 'test'; // 'x' is null or not an object


window.frames = 5; // Not implemented


var for; // expected identifier


// object doesn't support this property or method
document.doesNotExist(5);


alert(parseint('5')); // object expected

Most of these issues are caused by more common mistakes that you have to look out for:

  • Incorrect Capitilization

    All built-in JavaScript functions use something called Camel Case. Basically what this means is that all function names start with a lower case letter and the start of every subsequent word is upper-case: parseInt, getElementById, createElement, appendChild, etc.

    Since JavaScript is case-sensitive, typing one of these function names incorrectly will often result in a runtime error.

  • Referencing code, functions or DOM objects before they exist

    This problem usually occurs with respect to DOM objects. Let’s say you have code that modifies some form elements on your page. If you attempt to run this code before the form elements exist, i.e. if you put it in your <HEAD> tag, you will get a JavaScript error.

    This can usually be solved easily. The best-practice solution is to run your code on the onload event, ie:

    <BODY onload="loadFunction();>

    or better-yet, attaching an event to the Body onload.

  • Using a reserved word

    There is a long list of reserved JavaScript keywords. If you try to use many of these outside of their intended context, such as writing

    var for = 5;

    , you will get an error.

  • Using a missing parameter

    When you define a function, you usually expect a certain number of arguments to be passed to it. If one or more of these arguments is missing and you try to use it, you could run into some problems.

Most of these problems fall into the realm of typos or just general errors that must be fixed, but it’s good to know about them all so you don’t accidentally make one of these mistakes.

The last error type in this list however, using missing parameters, can be checked fairly easily:

function myFunction(a, b, c){
	if(a){
		// do stuff with a
	}
	if(b && c){
		// do stuff with b and c
	}
}

If our function is called with only one variable passed to it, there’s no problem. One thing to look out for is that if you are expecting a variable to come in as a value that may evaluate to false (such as 0 or false), your code will not run. Because of this, a better practice is to check if the variable is undefined:

function myFunction(a, b, c){
	if(typeof(a)!='undefined'){
		// do stuff with a
	}
	if((typeof(b)!='undefined') && (typeof(c)!='undefined')){
		// do stuff with b and c
	}
}

This way even if one of your variables is passed in as 0, false or null, your code will still run.

Moving on to actual error handling now, there are two ways to catch errors: a Try/Catch statement and the window.onerror function.

window.onerror

In all honesty the window.onerror function has very little practical use. Its biggest use is the ability to completely disable any runtime error messages:

window.onerror = function(){
	return true;
}

With this code you will never see a runtime error. Because of this, however, your application may not function. If a variable were null, for instance, and you performed an operation on it, you would normally get an error. With this code in place, your function or script would just silently stop running when the error took place.

You can also use window.onerror to display somewhat more friendly error messages to your users. You could simply display an alert stating ‘An error has occured, please contact the webmaster’, for instance, instead of showing the user all the technical details about the error (which most browsers do by default).

The one other use for window.onerror is to send yourself, the developer, a list of any errors that occur on your site. You could use AJAX to post error messages back to a form so that you can later go back and fix them. All this could be done behind the scenes without any user interaction.

Try/Catch/Finally & Throw

Try/Catch statements are, by far, the most common and usually best way to implement error handling in JavaScript. Not only that, but Try/Catch statements can sometimes be the only way to accomplish certain tasks such as object detection. Take a simple function to create an XMLHttp object in Internet Explorer, for instance:

var activeXObjects = ['Msxml2.XMLHTTP.6.0', 'Msxml2.XMLHTTP.5.0', 'Msxml2.XMLHTTP.4.0',
'Msxml2.XMLHTTP.3.0', 'Msxml2.XMLHTTP', 'Microsoft.XMLHTTP'];
for(var i=0; i<activeXObjects.length; i++){
	try{
		return new ActiveXObject(activeXObjects[i]);
	}catch(err){}
}

We don’t know ahead of time what objects the user has installed and unfortunately the browser gives us no way to detect this. So we are left with trying to create each of 6 possible objects until one of them (hopefully) works.

Try/Catch statements can be used to capture 2 types of errors: runtime errors and user errors. Runtime errors, as we explained, are when the JavaScript compiler has a problem with something you did. User errors, on the other hand, will technically run smoothly but because of the context of the application, shouldn’t. If you have a box that asks the user to enter their age, for instance, and they enter -2 this should throw an error.

The Try/Catch block has a fairly simple syntax:

try{
	// code
}catch(err){
	// error handling code
}

If the code in your try block comes up with an error, your script immediately jumps into the catch block. “err” will be a JavaScript Error object with several useful properties about the error: description, message, name and number that you can use to alert your user, or yourself, as to what happened:

try{
	var x;
	x[5] = 5;
}catch(err){
	alert('An error occured: '+err.description);
}

If you happen to have an error in your catch statement, JavaScript will revert to its built in error messages.

You can use this type of Try/Catch block of code anywhere you want. In general, however, code should be written in a way that makes this unnecessary, namely all input should be validated.

The Try/Catch block can also be used to create your own errors:

function setAge(x){
	if(typeof(x)=='undefined') throw('You must enter an age');
	if(typeof(x)!='number') throw('Your age must be a number');
	if(x<0) throw('Your age can not be less than 0');
	if(x>120) throw('Your age can not be greater than 120');

	var myAge = x;
	// more code here
}

try{
	setAge(userInput);
}catch(err){
	alert(err);
}

In this case we are validating the input that the user enters for an age. If they enter invalid data, our script immediately exits and they get an alert message telling them of their mistake.

The try/catch block has another piece, a “finale” statement:

try{
	// code
}catch(err){
	// code
}finally{
	// code
}

The code in the “finale block” will be executed no matter what happens with the Try/Catch statements. You might be wondering what the difference is between a finale block and simply putting code after the try/catch block. Well in most cases there is no difference at all. If your try/catch block is in a function, however, and either the try or catch statement returns, there is a big difference:

function myFunction(){
	try{
		return someValue;
	}catch(err){
		return defaultValue;
	}finally{
		alert('finale!');
	}

	alert('End!');
}

In this case both our try and catch scripts return a value. We will get the alert that says “finale!”, but we will not get the alert that says “End!” because our function will have already exited before the “End!” alert. The same concept holds true for Try/Catch statements that break out of or continue a for or while loop, such as:

for(var i=0; i<10; i++){
	try{
		if(i==5) continue;
	}catch(err){
		// error handling
	}finale{
		// code
	}

	// more code
}
AJAX Error Handling

XMLHttp requests, discussed in our last article, can have an entirely different type of error: the data simply didn’t come through. We can test for this by checking the status of our XMLHttp object:

function processingFunction(){
	if(oXml.readyState!=4) return; // our request is not done

	switch(oXml.status){
		case 0: case 200:	// request is good
			break;
		case 408: case 504: // request timed out
			// code
			break;
		default: // request error
			// code
			return; // you will probably want to exit
			break;
	}

	// continue with the request
}

oXml in this example is an XMLHttp object and the processingFunction has been attached to the onreadystatechange property of that object.

By checking the status code we know whether or not our request was successfully processed. A code of 200 is your standard HTTP “Everything went okay” status code. A case of 0 is what you get when loading files from your local file system (if you have permission to). You will often get a status code of 0 if you are testing an application locally.

Status codes of 408 and 504 represent timeout issues. Very often this points to network problems and simply trying the request again can often resolve the issue. Note, however, that these codes can also represent your server taking too long to respond. If you have a script error on your server that ends up in an infinite loop, for instance, you may get a 408 or 504 error code. In this case trying again would be detrimental, so be careful what you do here. The safest bet is to alert the user and exit the function, but this is not very user friendly.

All other error codes have their own meaning but we really don’t care what they are for this situation. All we care about is that we didn’t get back the data we need. So if our code gets into the “default” area, we know we have a problem. We should probably alert the user to this problem and exit our function.

That’s just about everything there is to JavaScript error handling. It is a good idea to include error handling in your functions, but you probably don’t need it on every function and piece of code. As long as you check input from your users you are usually safe in most instances. The most useful feature is using Try/Catch/Throw blocks of code to implement user validation.

Our next article will dive into a concept called recursion: “To understand recursion you must first understand recursion.”

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Popular Articles

Featured