Error Handling
JavaScript is decently equipped to get a handle on errors that may arise. The first tool we should know about is the
const myError = new Error('Oh No!');
throw myError.name + myError.message + myError.stack;
- SyntaxError: Unexpected token
- SyntaxError: illegal character
- ReferenceError: assignment to undeclared variable "x"
- ReferenceError: invalid assignment left-hand side
- RangeError: invalid date
The Error Object class can even be extended like any class (examples below). This could be used to tailor the content to the type of error; perhaps even omitting sensitive data from some errors. Below are a few examples of what could be made for specific purposes.
- AuthenticationError
- PermissionError
- DatabaseError
Error.prototype
will contain these properties
- constructor - This prototype's constructor function.
- message - A message concerning the error.
- name - The name of the error.
// Make an authentication error class extension
class AuthenticationError extends Error {
constructor(message) {
super(message)
this.name = 'AuthenticationError '
this.message = message
}
}
throw new AuthenticationError('An authentication error')
// RESULT: "AuthenticationError : An authentication error"
}
The Browsers Process
Below is a very basic look at how the browser will move through processing an error.
- ERROR!
- Is there a
catch (explained below)? - If no
catch is found, runonerror()
(in Node.js:process.on('uncaughtException')
Try-Catch Blocks
There are many approaches to handling the the Error Object. One common process is toTo put the above in more coding terms: try {}
, catch(obj) {}
, throw " "
and finally {}
will handle errors.
let cntr = 0;
function validateAge(elm) {
let output;
try {
let ageValue = elm.options[elm.selectedIndex].value;
if (ageValue == "" || ageValue == "0") {
throw "You must choose an age.";
}
if (ageValue < 18) {
throw ageValue + " is too young to vote.";
}
if (ageValue >= 18) {
throw ageValue + " is old enough to vote!";
}
} catch (obj) {
output = 'throw massage: "' + obj + '"
';
} finally {
cntr++;
output += 'This runs after TRY-CATCH.
This has run ' + cntr + ' times.';
document.getElementById('try-catch-output').innerHTML = output;
}
}
↓ More examples including extending the Error class ↓
try {
try {
somethingThatDoesntExist();
} catch (error) {
throw new Error(error)
}
} catch (error) {
console.log('got it!', error)
}
// RESULT: got it!Error: ReferenceError: somethingThatDoesntExist is not defined at < anonymous > : 5: 7)
// Another example
function fail() {
try {
console.log('this works');
throw new Error('Oh no!');
} catch (e) {
console.log('error', e);
} finally {
console.log('still good');
return 'returning from fail';
}
console.log('never going to get here'); // not reachable
}
fail();
// RESULT: this works
// Error: Oh no!
// at fail (:4:7)
// at :13:1
// still good
// "returning from fail"
////////////////////////
Extend the Error Class
///////////////////////
// Make an authentication error class extension
class AuthenticationError extends Error {
constructor(message) {
super(message)
this.name = 'ValidationError'
this.message = message
}
}
throw new AuthenticationError('An authentication error')
// RESULT: Uncaught ValidationError: An authentication error at <anonymous>:9:7
//A permission error class extension
class PermissionError extends Error {
constructor(message) {
super(message)
this.name = 'PermissionError'
this.message = message
this.favouriteSnack = 'grapes'
}
}
throw new PermissionError('A permission error')
// RESULT: Uncaught PermissionError: A permission error at <anonymous>:9:7
// A database error class extension
class DatabaseError extends Error {
constructor(message) {
super(message)
this.name = 'DatabaseError'
this.message = message
}
}
throw new DatabaseError('A database error')
// RESULT: Uncaught DatabaseError: A database error at <anonymous>:10:7
Async Error Handling
if an error occurs outside of the JavaScript engine, perhaps a variable that does not exist that is being used in a setTimeout()
, the error will not be captured using setTimeout()
is moved to the Call Stack.To handle asynchronous code errors like this,
catch()
ready!
const myError = new Error('Oh No!');
throw myError.name + myError.message + myError.stack;
// Using a Promise and catch to grab errors away missed in asynchronous code
Promise.resolve('asyncfail')
.then(response = & gt; {
console.log(response)
throw new Error('#1 fail')
})
.then(response = & gt; {
console.log(response)
})
.catch(err = & gt; {
console.error('error', err.message)
})
.then(response = & gt; {
console.log('hi am I still needed?', response)
return 'done'
})
.catch(err = & gt; {
console.error(err)
return 'failed'
})
// Using promises with async-await (more consise)
(async function() {
try {
await Promise.reject('oops!')
} catch (err) {
console.error(err)
}
console.log('This is still good!')
})()
Debugging Quick-take
Debugging can be done in the SOURCE tab of browser tools by setting breakpoints. As the detail of this can get increasingly involved, the following article is a better environment for a deeper dive rather than the limited space here: How To Pause Your Code With Breakpoints In Chrome DevTools
Notes on the Sources tab:
- In Chrome, the Sources tab also has a Call Stack area on the right to view the call stack.
- In this sources tab you can create a Script Snippet and add code to run.
- in a Script Snippet, you can write "debugger;" at a point to pause the execution (break point)
- eval()
- for...in loops
- with loops
- delete
- Inline Caching
- Not clearing adEventListeners() can cause memory leaks as these are usually attached to the global scope and will not be cleared via garbage collection.
- This is a great read on "hidden classes": LINK
- Managing Arguments. A good read on the subject: LINK
- Here is a great video that shows things that can make a compiler less efficient: LINK
Debugging via Console
The Console is a place to both run JavaScript and log results all while working within the browser. It is most useful for debugging JavaScript. There is a special Object built in to JavaScript that provides access to the console. Fittingly, this Object is called
- console.assert() - Log a message and stack trace to console if the first argument is false.
- console.clear() - Clear the console.
- console.count() - Log the number of times this line has been called with the given label.
- console.countReset() - Resets the value of the counter with the given label.
- console.debug() - Outputs a message to the console with the log level "debug".
- console.dir() - Displays an interactive listing of the properties of a specified JavaScript object. This listing lets you use disclosure triangles to examine the contents of child objects
- console.dirxml() - Displays an XML/HTML Element representation of the specified object if possible or the JavaScript Object view if it is not possible.
- console.error() - Resets the value of the counter with the given label.
- console.exception() - An alias for error().
- console.group() - Creates a new inline group, indenting all following output by another level. To move back out a level, call groupEnd().
- console.groupCollapsed() - Creates a new inline group, indenting all following output by another level. However, unlike group() this starts with the inline group collapsed requiring the use of a disclosure button to expand it. To move back out a level, call groupEnd().
- console.groupEnd() - Exits the current inline group.
- console.info() - Logging of informative detail. You may use additional arguments with this method.
- console.log() - For general output of logging information. You may use string substitution and additional arguments with this method.
- console.profile() - Starts the browser's built-in profiler (for example, the Firefox performance tool). You can specify an optional name for the profile.
- console.profileEnd() - Stops the profiler. You can see the resulting profile in the browser's performance tool (for example, the Firefox performance tool).
- console.table() - Displays tabular data as a table.
- console.time() - Starts a timer with a name specified as an input parameter. Up to 10,000 simultaneous timers can run on a given page.
- console.timeEnd() - Stops the specified timer and logs the elapsed time in seconds since it started.
- console.timeLog() - Logs the value of the specified timer to the console.
- console.timeStamp() - Adds a marker to the browser's Timeline or Waterfall tool.
- console.trace() - Outputs a stack trace.
- console.warn() - Outputs a warning message. You may use string substitution and additional arguments with this method.