Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
August 17, 2019 01:36 am GMT

Better error handling with async/await

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. - Colin Toh

Callback hell 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 is, but I'll give an example of how it looks.

User profile example 1

// Code that reads from left to right // instead of top to bottomlet 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 were introduced to the Javascript(ES6) language to handle asynchronous operations better without it turning into a callback hell.

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

User profile example 2

// A solution with promiseslet 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 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

// Arithmetic addition functionasync 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

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

async function userProfile() {  try {    let user = await getUser();    let friendsOfUser = await getFriendsOfUser(userId);    let posts = await getUsersPosts(userId);  } catch(e) {    console.log(e);  }  showUserProfilePage(user, friendsOfUser, posts);}

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

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

/** * @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 Error('Could not fetch user details');  let [friendsOfUser, friendErr] = await handle(    getFriendsOfUser(userId)  );  if(friendErr) throw Error('Could not fetch user\'s friends');  let [posts, postErr] = await handle(getUsersPosts(userId));  if(friendErr) throw 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

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.


Original Link: https://dev.to/sobiodarlington/better-error-handling-with-async-await-2e5m

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To