How to handle error propagation with async/await in JavaScript?

Content verified by Anycode AI
July 21, 2024
Flow control during asynchronous operations in modern JavaScript development is quite important, be it network requests, processing, or storage of data. Error handling in such asynchronous code is quite cumbersome and most of the time goes abnormally, causing problems in debugging. This guide provides a complete solution to the problem with \`async/await\`, being a modern syntax for working within asynchronous code. Use structured techniques of error handling and propagation to keep your code robust, readable, and maintainable. Learn how you can handle and propagate errors efficiently with a step-by-step example.

Handling error propagation with async/await in JavaScript is super important for keeping your code sturdy, easy to manage, and making sure it performs just right. So let's dive into how you can handle errors effectively using async/await.

Understanding Async/Await

Okay, let's start simple. async and await, introduced in ECMAScript 2017, are just fancy ways to work with Promises. An async function, well, returns a Promise. And the keyword await pauses the function's execution until the Promise is either resolved or rejected.

Basic Error Handling

Try/Catch Block

The straightforward way to handle errors in an async function? Just use a try/catch block.

async function fetchData() {
    try {
        let response = await fetch('https://api.example.com/data');
        if (!response.ok) {
            throw new Error('Network response was not ok ' + response.statusText);
        }
        let data = await response.json();
        return data;
    } catch (error) {
        console.error('There has been a problem with your fetch operation:', error);
    }
}

fetchData();

So, in this example:

  • The fetchData function is an async function.
  • await waits for fetch('https://api.example.com/data') to finish.
  • If the response isn't okay, it throws an error.
  • The catch block catches any errors from the try block, whether from the fetch call or parsing the response.

Propagating Errors

Sometimes, it's better to let the error bubble up to the caller. You skip catching it in the async function itself and catch where the function is invoked.

async function fetchData() {
    let response = await fetch('https://api.example.com/data');
    if (!response.ok) {
        throw new Error('Network response was not ok ' + response.statusText);
    }
    let data = await response.json();
    return data;
}

async function processData() {
    try {
        let data = await fetchData();
        console.log(data);
    } catch (error) {
        console.error('There has been a problem with the fetch operation:', error);
    }
}

processData();

Using Custom Error Classes

You can create custom error classes for more specific error handling.

class NetworkError extends Error {
    constructor(message) {
        super(message);
        this.name = 'NetworkError';
    }
}

async function fetchData() {
    let response = await fetch('https://api.example.com/data');
    if (!response.ok) {
        throw new NetworkError('Network response was not okay: ' + response.statusText);
    }
    let data = await response.json();
    return data;
}

async function processData() {
    try {
        let data = await fetchData();
        console.log(data);
    } catch (error) {
        if (error instanceof NetworkError) {
            console.error('Custom Network Error:', error.message);
        } else {
            console.error('Generic Error:', error.message);
        }
    }
}

processData();

Custom errors make it easier to identify different types of errors and handle them appropriately.

Combining Multiple Async Functions

When handling multiple async operations either in sequence or parallel, error handling becomes even more critical.

Sequential Execution

async function task1() {
    // some async logic
}

async function task2() {
    // some async logic
}

async function runTasks() {
    try {
        await task1();
        await task2();
        console.log('Tasks completed successfully');
    } catch (error) {
        console.error('Error occurred while executing tasks:', error);
    }
}

runTasks();

Parallel Execution with Promise.all

Running async tasks in parallel using Promise.all can boost efficiency, but error handling here is a bit different.

async function task1() {
    // some async logic
}

async function task2() {
    // some async logic
}

async function runTasks() {
    try {
        await Promise.all([task1(), task2()]);
        console.log('Both tasks completed successfully');
    } catch (error) {
        console.error('Error occurred in one of the tasks:', error);
    }
}

runTasks();

Logging and Tracking

In a real-world scenario, you might want to log errors to a monitoring service for further analysis.

async function fetchData() {
    let response = await fetch('https://api.example.com/data');
    if (!response.ok) {
        throw new Error('Network response was not ok ' + response.statusText);
    }
    let data = await response.json();
    return data;
}

async function processData() {
    try {
        let data = await fetchData();
        console.log(data);
    } catch (error) {
        // Log to some external service like Sentry, LogRocket etc.
        console.error('Log to service:', error);
        console.error('There has been a problem with the fetch operation:', error);
    }
}

processData();

Error handling with async/await in JavaScript means you need a good grasp of Promises and how async execution works. Using try/catch blocks, custom error classes, and proper logging, you can build reliable applications that handle unexpected hiccups gracefully. This approach ensures you catch and handle errors appropriately, whether they're from a single async operation or multiple. And with proper error propagation and logging, your code becomes a breeze to debug and maintain.

Have any questions?
Our CEO and CTO are happy to
answer them personally.
Get Beta Access
Anubis Watal
CTO at Anycode
Alex Hudym
CEO at Anycode