All the apps you build are perfect right? I know my apps never have unexpected issues…. said no one ever!
Errors are a part of development and what sets apart the good devs from the best devs is how you handle them. In our latest course JavaScript Error Handling jump head first into error handling essentials and best practices and learn how to become an error handling pro.
Here is a taste of what to expect in the course.
The basic syntax for handling errors in JavaScript involves the try-catch block. Here's a simple example:
try {
// Attempt to execute potentially risky code
const result = riskyOperation();
console.log(result);
} catch (error) {
// Handle any errors that occurred
console.error('An error occurred:', error.message);
} finally {
// This block always executes, regardless of errors
cleanup();
}
Should any error occur in the try block it will stop execution immediately and run the code within the catch block. Optionally you can also add a finally block to run code no matter how things go.
JavaScript has several built-in error types, each serving a specific purpose. In the course you’ll learn about some of the most common including:
ReferenceErrors occur when you try to use a variable that hasn't been declared:
try {
console.log(undefinedVariable);
} catch (error) {
console.log(error.name); // "ReferenceError"
console.log(error.message); // "undefinedVariable is not defined"
}
Or try to use a variable that’s not available in the current scope:
try {
function makePayment(){
const creditCard = "..."
}
console.log(creditCard);
} catch (error) {
console.log(error.name); // "ReferenceError"
console.log(error.message); // "creditCard is not defined"
}
SytanxErrors when the code violates JavaScript's syntax rules. Sometimes these can be caught with catch blocks like in the case of eval.
try {
eval('if (true) {'); // Incomplete if statement
} catch (error) {
console.log(error.name); // "SyntaxError"
}
In most cases though these are caught during the JS runtime’s first pass of the script causing programming execution to halt immediately. This makes sense as such errors don’t occur because of dynamic runtime data and/or conditions but due to typos in the code.
try{
if(true){ // Incomplete if statement
}catch(error){
// never runs, instead program execution halts
}
TypeErrors occur when an operation is performed on a value of the wrong type. For example trying to call toString()
on null
or padStart
on a boolean
.
try {
null.toString();
} catch (error) {
console.log(error.name); // "TypeError"
console.log(error.message); // "Cannot read property 'toString' of null"
}
Or if you try to re-assign the value of a constant.
try {
const youCantChangeMe = true
youCantChangeMe = false;
} catch (error) {
console.log(error.name); // "TypeError"
console.log(error.message); // "Assignment to constant variable.
}
Besides using the built-in error types, you can also create your own custom errors. Custom errors empower you to tailor your error handling strategy to the context of you unique applications.
For example, if you application required accepting payments from user’s you might end up create a custom PaymentError
.
// create custom errors by extending the base Error class
class PaymentError extends Error{
constructor(code, message){
super(message);
// Give your custom Errors intuitive names
this.name = "PaymentError"
// Add extra properties to best fit the use case
// such as custom error codes
this.code = code; // insufficient_funds, invalid_card, etc
// or user friendly messages based on error code
const codeMessageMap = {
'insufficient_funds': 'You do not have enough funds to complete this payment',
'invalid_card': 'The card number you entered is invalid'
}
this.userMessage = codeMessageMap[code] || "An error occurred with your payment"
}
}
Async error handling of the fetch function requires special attention. Why? Because it’s the source of the majority of errors. Plus it comes with some common gotchas.
One gotcha we see on the regular is developers forgetting to await the result of fetch causing the catch block to never run (even when errors are thrown).
try {
// 😱 forgot to await!
fetch('/api/user', {
method: 'POST'
});
} catch (error) {
// never runs even if fetch fails
}
Another mistakes developers regularly make is expecting a non-200 status code (like a 500 or a 404) to throw an error and be caught by the catch block. But that’s not how fetch
works! It only throws errors on network level errors. If you want to catch an error for a 500 status code you have to do it yourself with res.ok
try {
const response = await fetch('/api/user'); // returns 500 status code
// ⚠️ without this check catch would never run!
if(!response.ok){
throw new Error("Response NOT ok")
}
} catch (error) {
console.log(error)
}
During the course we also look at techniques designed specifically for working with fetch such as automating retries and setting custom request timeouts. (HINT: you can do this manually (and we’ll show you how) or you can reach for one of our favorite fetch wrapper libraries.)
Besides giving the language level tools for good error handling, we’ll also dive into some more high level best practices for creating apps that fail gracefully, minimizing user frustration, and giving intuitive paths to recovery. A few strategies include:
Always translate technical errors into user-friendly messages for display to end users. At most a user will understand what a 404 status code is otherwise your messages should be very practical and high level.
User friendly message’s aren’t as useful for developers. They can be included but developers also need as many technical details a possible to track down what the underlying issue is and fix it!
Also remember that developers don’t have the luxury of standing over end user’s shoulders and opening up their consoles. Therefore make sure all errors get reported to remote servers for developer visibility and scrutiny.
During development it’s awfully tempting to ignore errors by swallowing them with empty catch blocks. 99 times out of 100 this is the wrong approach and does nothing more than hide your user’s issues from the eyes of those who are in a position to fix them.
// ❌ Bad practice
try {
riskyOperation();
} catch (error) {
// Empty catch block silently ignores errors
}
All the topics above and more can be explored in more detail in our comprehensive video course JavaScript Error Handling. Join Daniel as he walks you through JavaScript error handling essentials and best practices in this must watch course.
Our goal is to be the number one source of Vue.js knowledge for all skill levels. We offer the knowledge of our industry leaders through awesome video courses for a ridiculously low price.
More than 200.000 users have already joined us. You are welcome too!
© All rights reserved. Made with ❤️ by BitterBrains, Inc.