Better error handling with async/await
Tags: javascript, webdev, asyncawait, promises
Published: 2019-08-17 01:36:01

---
title: Better error handling with async/await
published: true
description: Not using try/catch with async/await could result to (node:11) DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code. */

tags: #javascript #webdev #asyncawait #promises
---


This article is intended to suggest a better way to handle errors when using async/await syntax. Prior knowledge of how promises work is important.


##From Callback Hell to Promises

> [Callback Hell, also known as Pyramid of Doom, is an anti-pattern seen in code of programmers who are not wise in the ways of asynchronous programming.](https://colintoh.com/blog/staying-sane-with-asynchronous-programming-promises-and-generators) - Colin Toh

[Callback hell](http://callbackhell.com/) makes your code drift to the right instead of downward due to multiple nesting of callback functions.

I wont go into details of what [callback hell](http://callbackhell.com/) is, but I'll give an example of how it looks.


#### User profile example 1
```js
// Code that reads from left to right 
// instead of top to bottom

let user;
let friendsOfUser;

getUser(userId, function(data) {
  user = data;

  getFriendsOfUser(userId, function(friends) {
  	friendsOfUser = friends;

  	getUsersPosts(userId, function(posts) {
  	  showUserProfilePage(user, friendsOfUser, posts, function() {
  		// Do something here

  	  });
  	});
  });
});
```

## Promises

[Promises](https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-promise-27fc71e77261) were introduced to the Javascript(ES6) language to handle asynchronous operations better without it turning into a [callback hell](http://callbackhell.com/).

The example below use promises to solve callback hell by using multiple chained `.then` calls instead of nesting callbacks.

#### User profile example 2
```js
// A solution with promises

let user;
let friendsOfUser;

getUser().then(data => {
  user = data;

  return getFriendsOfUser(userId);
}).then(friends => {
  friendsOfUser = friends;

  return getUsersPosts(userId);
}).then(posts => {
  showUserProfilePage(user, friendsOfUser, posts);
}).catch(e => console.log(e));

```

The solution with promise looks cleaner and more readable.


##Promises with with async/await

[Async/await](https://javascript.info/async-await) is a special syntax to work with promises in a more concise way. 
Adding async before any `function` turns the function into a promise. 

>All `async` functions return promises.

#### Example
```js
// Arithmetic addition function
async function add(a, b) {
  return a + b;
}

// Usage: 
add(1, 3).then(result => console.log(result));

// Prints: 4
```

Making the `User profile example 2` look even better using async/await

#### User profile example 3
```js
async function userProfile() {
  let user = await getUser();
  let friendsOfUser = await getFriendsOfUser(userId);
  let posts = await getUsersPosts(userId);

  showUserProfilePage(user, friendsOfUser, posts);
}
```

### Wait! there's a problem

If theres a promise rejection in any of the request in `User profile example 3 `, `Unhandled promise rejection` exception will be thrown.

Before now Promise rejections didn't throw errors. Promises with unhandled rejections used to fail silently, which could make debugging a nightmare.

Thank goodness promises now throws when rejected.


- **Google Chrome throws:** `VM664:1 Uncaught (in promise) Error`

- **Node will throw something like:** `(node:4796) UnhandledPromiseRejectionWarning: Unhandled promise rejection (r                                                                                                     ejection id: 1): Error: spawn cmd ENOENT
[1] (node:4796) DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.`

> No promise should be left uncaught. - *Javascript*

Notice the `.catch` method in `User profile example 2 `. 
Without the .catch block Javascript will throw `Unhandled promise rejection` error when a promise is rejected.

Solving this issue in `User profile example 3 ` is easy. `Unhandled promise rejection` error can be prevented by wrapping await operations in a try...catch block:

#### User profile example 4
```js
async function userProfile() {
  try {
    let user = await getUser();
    let friendsOfUser = await getFriendsOfUser(userId);
    let posts = await getUsersPosts(userId);

    showUserProfilePage(user, friendsOfUser, posts);
  } catch(e) {
    console.log(e);
  }
}

```

*Problem solved!*

### ...But error handling could be improved

*How do you know with error is from which async request?*

We can call a `.catch` method on the async requests to handle errors. 

#### User profile example 5
```js
let user = await getUser().catch(e => console.log('Error: ', e.message));

let friendsOfUser = await getFriendsOfUser(userId).catch(e => console.log('Error: ', e.message));

let posts = await getUsersPosts(userId).catch(e => console.log('Error: ', e.message));

showUserProfilePage(user, friendsOfUser, posts);
```

The solution above will handle individual errors from the requests, but its a mix of patterns. There should be a cleaner way to use async/await without using `.catch` method (Well, you could if you don't mind).


## Here's my solution to a better async/await error handling 

#### User profile example 6
```js
/**
 * @description ### Returns Go / Lua like responses(data, err) 
 * when used with await
 *
 * - Example response [ data, undefined ]
 * - Example response [ undefined, Error ]
 *
 *
 * When used with Promise.all([req1, req2, req3])
 * - Example response [ [data1, data2, data3], undefined ]
 * - Example response [ undefined, Error ]
 *
 *
 * When used with Promise.race([req1, req2, req3])
 * - Example response [ data, undefined ]
 * - Example response [ undefined, Error ]
 *
 * @param {Promise} promise
 * @returns {Promise} [ data, undefined ]
 * @returns {Promise} [ undefined, Error ]
 */
const handle = (promise) => {
  return promise
    .then(data => ([data, undefined]))
    .catch(error => Promise.resolve([undefined, error]));
}

async function userProfile() {
  let [user, userErr] = await handle(getUser());

  if(userErr) throw new Error('Could not fetch user details');

  let [friendsOfUser, friendErr] = await handle(
    getFriendsOfUser(userId)
  );

  if(friendErr) throw new Error('Could not fetch user\'s friends');

  let [posts, postErr] = await handle(getUsersPosts(userId));

  if(postErr) throw new Error('Could not fetch user\'s posts');

  showUserProfilePage(user, friendsOfUser, posts);
}
```

Using the `handle` utility function, we are able to avoid `Unhandled promise rejection` error and also handle error granularly. 

###Explanation
The `handle` utility function takes a promise as an argument and always resolves it, returning an array with `[data|undefined, Error|undefined]`. 

- If the promise passed to the `handle` function resolves it returns `[data, undefined]`;
- If it was rejected, the `handle` function still resolves it and returns `[undefined, Error]`

## Similar solutions
- [Easier Error Handling Using Async/Await](https://medium.com/@jesterxl/easier-error-handling-using-async-await-b9ab0cb938e) - [Jesse Warden](https://twitter.com/jesterxl)
- [NPM Package - await-to-js](https://www.npmjs.com/package/await-to-js) 

## Conclusion
Async/await has a clean syntax, but you still have to handle thrown exceptions in async functions.

Handling error with `.catch` in promise `.then` chain can be difficult unless you implement custom error classes.

Using the `handle` utility function, we are able to avoid `Unhandled promise rejection` error and also handle error granularly.