DEV Community

Tasnim Reza
Tasnim Reza

Posted on

3 ways to handle Async/await error.

When comes to asynchronous operation, we always think about JavaScript Promise methods right? But there are other ways we can sort this out. I found the most profound way is async/await. async is used when we are writing any asynchronous operation and await is used when we are waiting for that asynchronous operation.

const getPersons = async () => {
    // we are making the actual database ie REST api call
    return fetch('persons');
}

const process = async () => {
    // we are waiting for persons
    const persons = await getPersons();
    console.log(persons);
}

There are a number of ways we can handle errors in JavaScript. In this article, we'll talk about the 3 most used ways to handle async/await error.

1. Promise.catch

We know we can use promises with async. We'll add a catch block with the asynchronous method.

const getPersons = async () => {
    return fetch('http://person')
        // here we added a promise catch method
        .catch(error => {
            //TypeError: Failed to fetch
            console.log(error);
        });
}

const process = async () => {
    const persons = await getPersons();
    console.log(persons); // undefined
}

process();

This way we can handle unhandled promise rejection error. But the downside of this approach is we have to add a catch method for each asynchronous operation.

2. A wrapper of Promise.catch

We can create a wrapper function so that we don't need to add the catch method of each asynchronous operation.

const asyncWrapper = async (promise) => {
    return promise.catch(error => {
        console.log(error); //TypeError: Failed to fetch
        return error;
    });
}

const getPersons = async () => {
    return asyncWrapper(fetch('http://person'));
}

const process = async () => {
    const persons = await getPersons();
    console.log(persons); //TypeError: Failed to fetch
}

process();

It is better hah? So, what we did here? We extract the catch method from getPersons to asyncWrapper. We handle the error inside the wrapper and return the error to the outside to show some error message to the user.

In this approach there are slight difficulties, each and every time we have to check, there is a valid person or not? This is tedious and there is no clear way to know that there is an error. We can solve this problem

const asyncWrapper = async (promise) => {
    return promise
        .then(result => [null, result])
        .catch(error => [error]);
}

const getPersons = async () => {
    return asyncWrapper(fetch('http://person'));
}

const process = async () => {
    const [error, result] = await getPersons();
    console.log(error, result); //TypeError: Failed to fetch, undefined
}

process();

This is way better right? Here we use JavaScript destructing pattern. We add a .then method and return the success case as [null, result]. That means, if your asynchronous operation is successful, it will return the result where the error is null

On the other hand, we returned [error] means the result is undefined. It is other way around, when error is present there is no result and when the result is present there is no error! In this pattern, we clearly know when an error happened.

3. Promise.allSettled

We know Promise.allSettled is very powerful method where it shows the reason why your asynchronous operation is failed. If you want to learn more, check this article

https://dev.to/tasnimreza/3-most-powerful-javascript-promise-methods-2g8f

const asyncWrapper = async (promise) => {
    const [result] = await Promise.allSettled([promise]);
    return result;
}

const getPersons = async () => {
    return asyncWrapper(fetch('http://person'));
}

const process = async () => {
    const { reason, value } = await getPersons();
    console.log(reason, value); //TypeError: Failed to fetch, undefined
}

process();

We use Promise.allSettled inside the wrapper and await to finish the job. When the asynchronous operation is finished, it destructing the result to the result array. Because Promise.allSettled always return array.

Finally we use object destructing to receive the result. As we know Promise.allSettled return status, reason, value. Here we ignore the status because it is not needed.

When the asynchronous operation is failed, reason is present and value is undefined. If the call is successful, value is present and reason is undefined. In this way we clearly know when the error is present.

Note: There is a common way to handle error in JavaScript is using try...catch I skip this because it is quiet well known pattern.

Summery

Thanks for reading and I hope you learn something from this article. If you have any question please let me know in the comments. I hope your code will be much better after using any of these pattern!

Top comments (0)