Friday, 3 August, 2018 UTC


Summary

Good code is code that preemptively prevents errors in a program from crashing the program and instead handles those errors gracefully by doing something like recovering from the error, informing the user about it or logging the error somewhere. This is called error handling and the primary mechanism for error handling in JavaScript is the try…catch construct.
Let’s briefly go over how to use try…catch, and then also look at try…catch…finally and the throw statement to throw our own custom errors.
try…catch
Here’s the basic syntax for try…catch:
try { // some code that my error-out } catch (e) { // this will run only if the code in the try block errors-out } 
Notice how the catch block gets access to the error object (e in the above example). The error object will have a name and a message property. In most environments the error object will also have a stack property with the stack trace leading to the error.
If you log the error object to the console, it’s name and message properties will conveniently be concatenated together.
Here’s an example where we misspell a variable name:
let myVariable = 2; try { console.log(myVriable + 77); } catch (e) { console.log('Oopsies -', e); } 
In this case, the catch block will be executed and the following message is printed to the console:
Oopsies - ReferenceError: myVriable is not defined 
try...catch only handles errors encountered at runtime, and invalid JavaScript will error-out at parse-time so your program won't run at all. Instead, use a linter like ESLint to catch those errors directly when authoring your code.
Throwing Custom Errors
Oftentimes instead of catching pure JavaScript errors we instead will want to catch conditions encountered in our program that should be considered errors. For this, we can make use of the throw statement to throw our own errors in a try block so that the error can be caught and dealt with:
let myVariable = prompt('Give me a number'); try { if (isNaN(+myVariable)) { // throws if the value provided can't be coerced to a number throw new Error('Not a number!'); } console.log('Good choice', myVariable); } catch (e) { console.log(e); } 
With the above example, if we don’t enter a value that can be coerced to a number we throw a custom error that’s then caught by our catch clause.
You can also see that we make use of the Error constructor to create an error object with a custom message passed-in as the first argument. In this case, our error object will have a name of Error, but we can also produce a specific error type:
try { if (isNaN(+myVariable)) { throw new TypeError('Not a number!'); } console.log('Good choice', myVariable); } catch (e) { console.log(e); }

Rethrowing an error

A good coding practice is to catch and deal only errors that we expect and then to rethrow other errors to be handled by a potential parent try…catch construct:
let myVariable = prompt('Give me a number'); try { if (isNaN(+myVariable)) { throw new TypeError('Not a number!'); } console.log('Good choice', myVariable); } catch (e) { if (e.name === 'TypeError') { console.log(e); } else { throw e; } } 
With the above example, if an error different than our TypeError is encountered in the try clause, our catch clause rethrows the error to be dealt with using a parent try…catch construct that we would put in place.
try…catch…finally
You can also tack-on a finally block to ensure that some code runs no matter if the code in the try blocks errors-out or not:
let myVariable = 2; try { console.log(myVriable + 77); } catch (e) { console.log('Oopsies -', e); } finally { console.log('Runs no matter what'); } 
The finally block can be useful to clean-up your code.
try…finally
Like finally, the catch clause is optional and, if you want you can use try…finally to ensures that some code runs upon encountering an error.
Here for example we try to set the background color on an element that doesn’t exist. First, using a try…catch…finally:
try { document.querySelector('.not-there').style.backgroundColor = 'pink'; } catch (e) { console.log('Oh no -', e); } finally { console.log('Finally runs'); } console.log('After try block'); // Oh no - TypeError: Cannot read property 'style' of null // Finally runs // After try block 
And now using only try…finally:
try { document.querySelector('.here2').style.backgroundColor = 'pink'; } finally { console.log('Finally runs'); } console.log('After try block'); // Finally runs // Uncaught TypeError: Cannot read property 'style' of null 
Notice how, when the catch clause is missing, the error is uncaught and the program crashes, but only after running the code in our finally clause.