Better error handling with async/await, by Sobio Darlington

This article describes a better way to handle errors when using the async/await syntax. Before you do that, you need to take a look at how Promise works.

From callback hell to Promise

Callback Hell, also known as the “Pyramid of Doom,” is an anti-pattern seen in developer code, and asynchronous programming is not wise. – Colin Toh

Because of the nesting of callback functions, callback hell causes your code to be laid out to the right instead of straight down.

To visualize the callback function, here is an example.


User profile Case 1

// 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

      });
    });
  });
});
Copy the code

Promise

Promise is a language feature introduced in ES2015 (commonly known as ES6) to better handle asynchronous operations and avoid callback hell.

The following example uses Promise’s.then chain to solve the callback hell problem.

User profile Case 2

// 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));
Copy the code

Promise processing is cleaner and more readable.

async/await Promise

Async /await is a special syntax that deals with promises in a more concise way.

Adding the async keyword before funtion converts the function to a Promise.

All async functions return a Promise.

example

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

// Usage: 
add(1.3).then(result= > console.log(result));

// Prints: 4
Copy the code

Use async/await to make “user profile Case 2” look better.

User profile Case 3

async function userProfile() {
  let user = await getUser();
  let friendsOfUser = await getFriendsOfUser(userId);
  let posts = await getUsersPosts(userId);

  showUserProfilePage(user, friendsOfUser, posts);
}
Copy the code

Wait a minute! There is a problem

In User Profile Case 3, if there is a Promise Reject, the Unhandled Promise Rejection exception is thrown.

None of the code written up to this point considered the Promise Reject case. Untreated Reject promises have failed silently in the past, which can make debugging a nightmare.

Now, however, Promise Reject throws an error.

  • Error thrown by Google Chrome:VM664:1 Uncaught (in promise) Error
  • Node throws errors like this:(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 Case 2”. If no. Catch block is written, JavaScript throws an Unhandled Promise Rejection error when it promises Reject.

Dealing with the problem in “User Profile Case 3” is relatively easy. Just use try… The catch block wraps the await statement to avoid Unhandled Promise rejection.

User profile Case 4

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); }}Copy the code

Problem solved!

But could error handling be more elegant?

How do I know which asynchronous request the error came from?

You can use the.catch method on asynchronous requests to handle errors.

User profile Case 5

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);
Copy the code

The above solution handles a single error from the request, but uses a mix of patterns. There should be a cleaner way to use async/await without using the.catch method (well, you can do that if you don’t mind).

My better async/await error handling scheme

User profile Case 6

/** * @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);
}
Copy the code

A utility function called Handle is used to avoid an Unhandled Promise rejection error and to fine-tune the handling of the error.

explain

Handle function accepts a Promise object as a parameter, and always resolve it, at [data | undefined, the Error | undefined] returns the result.

  • If the Promise resolves,handleThe function returns[data, undefined];
  • If Promise reject comes through,handleThe function returns[undefined, Error].

A similar solution

  • How easier to handle errors in async/await, Jesse Warden
  • NPM package – await – to – js

conclusion

Async /await syntax is neat, but you still have to deal with errors thrown in asynchronous functions.

Unless you implement custom error classes, it is difficult to handle.catch error handling in the promise.then chain.

Using the Handle utility function, we can avoid an Unhandled Promise rejection error and also handle the error in a fine-grained way.

(End of text)


Advertising time (long term)

I have a good friend who owns a cattery, and I’m here to promote it for her. Now the cattery is full of Muppets. If you are also a cat lover and have the need, scan her FREE fish QR code. It doesn’t matter if you don’t buy it. You can just look at it.

(after)