Assembly in Progress...

Error Handling & Debugging

Error Handling

JavaScript is decently equipped to get a handle on errors that may arise. The first tool we should know about is the Error Object.

const myError = new Error('Oh No!');
throw myError.name + myError.message + myError.stack;
Built-in Error Objects are numerous. A complete list can be found here → A few commonly encountered are:

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

Usually, the Error.prototype will contain these properties
  • constructor - This prototype's constructor function.
  • message - A message concerning the error.
  • name - The name of the error.
Here is a very basic example of the start of extending the Error class:
// 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.

  1. ERROR!
  2. Is there a catch (explained below)?
  3. If no catch is found, run onerror() (in Node.js: process.on('uncaughtException')

Try-Catch Blocks

There are many approaches to handling the the Error Object. One common process is to catch and throw. With this, code is in place to fire if the code it is attached to encounters an error. This is the catch. Once we have the error, we can throw whatever data we want to trigger various possible actions. finally, is a function that runs whether an error occurs or not or not. It can be used to close connections or otherwise cleanup.

To 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;
}
}
↓ Try it out live ↓ CHOOSE AN AGE →
-Error output here-

↓ 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 catch because that code would have already run when the output from setTimeout() is moved to the Call Stack.To handle asynchronous code errors like this, Async - Await can be used.

Note: Make sure to always have a catch() ready!
const myError = new Error('Oh No!');
throw myError.name + myError.message + myError.stack;
↓ Example: ↓
 // 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)
Potential Performance Issues
  • 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

Console Methods
  • 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.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

q
↑ Back to Top