Error handling, like many aspects of JavaScript, has been maturing since
the dark ages of Netscape and IE4.
No longer are you forced to settle for what the browser throws in your face
in an event of a JavaScript error, but instead can take the matter into your
own hands. The
try/catch/finally statement of JavaScript lets
you dip your toes into error prune territory and "reroute" when a
JavaScript "exception" is
encountered. Along with other defensive coding techniques such as
Object
detection and the onError event, try/catch/finally
adds the ability to
navigate around certain errors that in the past would have instantly stopped
your script at its tracks. No more!
try/catch/finally
try/catch/finally are so called exception handling
statements in JavaScript. An exception is an error that occurs at
runtime due to an illegal operation during execution. Examples of
exceptions include trying to reference an undefined variable, or calling
a non existent method. This versus syntax errors, which are
errors that occur when there is a problem with your JavaScript syntax.
Consider the following examples of syntax errors versus exceptions:alert("I am missing a closing parenthesis //syntax erroralert(x) //exception assuming "x" isn't defined yetundefinedfunction() //exception
try/catch/finally lets you deal with exceptions gracefully.
It does not catch syntax errors, however (for those, you need to use
the onerror event). Normally whenever the browser runs into an
exception somewhere in a JavaScript code, it displays an error message to
the user while aborting the execution of the remaining code. You can put
a lid on this behaviour and handle the error the way you see
fit using try/catch/finally. At its simplest you'd
just use try/catch to try and run some code, and in the
event of any exceptions, suppress them:
try{
undefinedfunction()
}
catch(e){
//catch and just suppress error
}
undefinedfunction()
}
catch(e){
//catch and just suppress error
}
Assuming
undefinedfunction() is undefined, when the browser
runs the above, no errors will be shown. The syntax for try/catch/finally
is
a try clause followed by either a catch or finally clause (at least one or
both of them). The catch clause if defined traps any errors that has
occurred
from try, and is indirectly passed the error object that
contains additional info about the error. Lets see a slightly more complex
example now:
try{
undefinedfunction()
alert('I guess you do exist')
}
catch(e){
alert('An error has occurred: '+e.message)
}
undefinedfunction()
alert('I guess you do exist')
}
catch(e){
alert('An error has occurred: '+e.message)
}
Click on the above button, and notice how only "An Error has occurred" alert pops up, but not "I guess you do exist". This tells us that when
try
encounters an error, it immediately skips any remaining code inside it and
goes straight to catch. The default error message is obviously suppressed,
though you can still retrieve this information by accessing the Error object
that gets indirectly passed into catch. We'll look at the Error object in
detail on the next page.There's another clause,
finally, that if defined will be
executed regardless of whether an error occurs in the try
clause proceeding it:
try{
undefinedfunction()
alert('I guess you do exist')
}
catch(e){
alert('An error has occurred: '+e.message)
}
finally{
alert('I am alerted regardless of the outcome above')
}
undefinedfunction()
alert('I guess you do exist')
}
catch(e){
alert('An error has occurred: '+e.message)
}
finally{
alert('I am alerted regardless of the outcome above')
}
finally can be useful when you need to "clean up" after some
code inside try. While it's true finally will always be
executed if defined, certain statements inside try such as continue,
break,
return, or when an error has occurred and there is no catch clause will all
cause finally to be executed immediately thereafter. In the following
example, the value "5" is alerted, since control is handed over to
finally
when i reaches 5 inside try:
try{
for (var i=0; i<10; i++){
if (i==5)
break
x=i
}
}
finally{
alert(i) //alerts 5
}
for (var i=0; i<10; i++){
if (i==5)
break
x=i
}
}
finally{
alert(i) //alerts 5
}
Nested try/catch/finally statements
As a reminder, try should never be defined just by itself, but always
followed by either catch, finally, or both. Within each clause, you can
define additional try/catch/finally statements following the same
aforementioned rule. Take the instance where an error has occurred within the
catch clause- defining an additional try/catch statement inside it takes care of it:
var ajaxrequest=null
if (window.ActiveXObject){ //Test for support for different versions of ActiveXObject in IE
try {
ajaxrequest=new ActiveXObject("Msxml2.XMLHTTP")
}
catch (e){
try{
ajaxrequest=new ActiveXObject("Microsoft.XMLHTTP")
} //end inner try
catch (e){
alert("I give up. Your IE doesn't support Ajax!")
} //end inner catch
} //end outer catch
}
else if (window.XMLHttpRequest) // if Mozilla, Safari etc
ajaxrequest=new XMLHttpRequest()
ajaxrequest.open('GET', 'process.php', true) //do something with request
if (window.ActiveXObject){ //Test for support for different versions of ActiveXObject in IE
try {
ajaxrequest=new ActiveXObject("Msxml2.XMLHTTP")
}
catch (e){
try{
ajaxrequest=new ActiveXObject("Microsoft.XMLHTTP")
} //end inner try
catch (e){
alert("I give up. Your IE doesn't support Ajax!")
} //end inner catch
} //end outer catch
}
else if (window.XMLHttpRequest) // if Mozilla, Safari etc
ajaxrequest=new XMLHttpRequest()
ajaxrequest.open('GET', 'process.php', true) //do something with request
Here I'm using a nested
try/catch statement to try and determine in IE
which version of the ActiveX Object it supports that's needed to initialize
an Ajax request. Using object detection won't work here, since the issue
isn't whether the browser supports ActiveXObject here, but
which version.The Error object and throwing your own errors
The Error Object
As promised, we're going to take a closer look at the
Error object that
gets passed into the catch clause to see just what we can
extract from it in an event of an error. The Error object in
all browsers support the following two properties:- name: The name of the error, or more specifically, the name of the constructor function the error belongs to.
- message: A description of the error, with this description varying depending on the browser.
try{
document.body.filters[0].apply()
}
catch(e){
alert(e.name + "\n" + e.message)
}
document.body.filters[0].apply()
}
catch(e){
alert(e.name + "\n" + e.message)
}
name property,
which as mentioned correspond to the names of the error's constructors.
They are:| Error Name | Description |
|---|---|
| EvalError | An error in the eval() function has
occurred. |
| RangeError | Out of range number value has occurred. |
| ReferenceError | An illegal reference has occurred. |
| SyntaxError | A syntax error within code inside the eval()
function has occurred. All other syntax errors are not caught by
try/catch/finally, and will trigger the default browser
error message associated with the error. To catch actual syntax errors,
you may use the onerror event. |
| TypeError | An error in the expected variable type has occurred. |
| URIError | An error when encoding or decoding the URI has occurred
(ie: when calling encodeURI()). |
This level of detail may be useful when you wish to sniff out a specific type of error in your
catch clause. In the below, no
DIV on the page exists with ID="mydiv". When trying to set its
.innerHTML property, a TypeError occurs, since
we're trying to assign the .innerHTML property to a null
object:
try{
document.getElementById("mydiv").innerHTML='Success' //assuming "mydiv" is undefined
}
catch(e){
if (e.name.toString()=="TypeError") //evals to true in this case
//do something
}
document.getElementById("mydiv").innerHTML='Success' //assuming "mydiv" is undefined
}
catch(e){
if (e.name.toString()=="TypeError") //evals to true in this case
//do something
}
Ok, so maybe it's not that useful most of the time, but you just never know.
Throwing your own errors (exceptions)
Instead of waiting for one of the 6 types of errors above to occur before
control is automatically transferred from the try block to the
catch block, you can also explicitly throw your own exceptions
to force that to happen on demand. This is great for creating your own
definitions of what an error is and when control should be transferred to
catch.To throw an error, invoke, well, the
throw statement inside
your try/catch/finally blocks. The syntax is:
throw myerrorobject
Where myerrorobject can in fact be anything from a string,
number, Boolean, to a new or one of the 6 default Error Constructor
functions. What myerrorobject is set to mainly just affects what
error.name and
error.message returns in your catch clause. Most
commonly you would just throw a new Error object:throw new Error("Oh oh, an error has occured")
throw in action:
function entrycheck(){
try{
var agecheck=prompt("This movie is rated PG-13. Please enter your age before continuing:")
if (isNaN(parseInt(agecheck)))
throw new Error("Please enter a valid age")
else if (agecheck<13)
throw new Error("Sorry, but you are too young for this movie")
alert("Enjoy the movie!")
}
catch(e){
alert(e.name+" "+e.message)
}
}
try{
var agecheck=prompt("This movie is rated PG-13. Please enter your age before continuing:")
if (isNaN(parseInt(agecheck)))
throw new Error("Please enter a valid age")
else if (agecheck<13)
throw new Error("Sorry, but you are too young for this movie")
alert("Enjoy the movie!")
}
catch(e){
alert(e.name+" "+e.message)
}
}
Try entering a none numeric value (ie: "haha") or a number less than 13 (ie: 11). In both cases, by using
throw, control is instantly
transferred to catch, with e.message displaying a
different message. Technically entering a string or number less than 13
certainly doesn't constitute an exception in JavaScript, though for our
purpose here, they should. That's how throw can be useful- when
you need to specify your own parameters of what an error is inside
try/catch/finally.As mentioned, there are a number of other things apart from
new
Error() you can throw, which changes the contents of the error object
passed into catch. The following are all valid throws:- throw "An error has occurred"
- throw true
- throw new Error("I detect an error!")
- throw new SyntaxError("Your syntax is no good")
SyntaxError with
one of the 6 Error constructor function names to throw a specific type of
error. In our age check example above, we could have thrown a
SyntaxError when the value entered was a string, and a
RangeError when the value was less than 13:
function entrycheck(){
try{
var agecheck=prompt("This movie is rated PG-13. Please enter your age before continuing:")
if (isNaN(parseInt(agecheck)))
throw new SyntaxError("Please enter a valid age")
else if (agecheck<13)
throw new RangeError("Sorry, but you are too young for this movie")
alert("Enjoy the movie!")
}
catch(e){
alert(e.name+" "+e.message)
}
}
This has the effect of changing what try{
var agecheck=prompt("This movie is rated PG-13. Please enter your age before continuing:")
if (isNaN(parseInt(agecheck)))
throw new SyntaxError("Please enter a valid age")
else if (agecheck<13)
throw new RangeError("Sorry, but you are too young for this movie")
alert("Enjoy the movie!")
}
catch(e){
alert(e.name+" "+e.message)
}
}
e.name returns-
SyntaxError and RangeError, respectively. If that's not
enough, you can even throw a generic Error object with custom name and
message properties:
throw{
name: "JavaScriptKit Error",
message: "Error detected. Please contact webmaster"
}
name: "JavaScriptKit Error",
message: "Error detected. Please contact webmaster"
}
No comments :
Post a Comment