More Elegant Error Handling in JavaScript async await

  • 2021-11-23 22:15:24
  • OfStack

Directory background Why error handling async await More Elegant Error Handling Summary Summarize

Background

The team came to a new partner and found that in our team code specification, we should add try... catch to async await. He felt puzzled. If there are many places (not concentrated), isn't it necessary to add many places? Isn't that very inelegant?

Why error handling

JavaScript is a single-threaded language. If try... catch is not added, it will cause direct error reporting and cannot continue execution. Of course, it doesn't mean that 1 in your code must be wrapped in try... catch. Using try... catch means that you know that this location code is likely to report errors, so you use try... catch to capture and let the program continue to execute.

I understand that when we execute async await, we run in an asynchronous scenario, and this scenario should not block the process, so it is recommended to use try... catch processing.

async await More Elegant Error Handling

But it is true that as my colleague said, adding try... catch is not a very elegant behavior. Therefore, I found that How to write async await without try-catch blocks in Javascript mentioned a more elegant method of processing and packaged it into a library-await-to-js. This library has only one function, so we can fully apply this function to our business, as follows:


/**
 * @param { Promise } promise
 * @param { Object= } errorExt - Additional Information you can pass to the err object
 * @return { Promise }
 */
export function to<T, U = Error> (
  promise: Promise<T>,
  errorExt?: object
): Promise<[U, undefined] | [null, T]> {
  return promise
    .then<[null, T]>((data: T) => [null, data]) //  Successful execution, return the array 1 Items are  null . No. 1 2 One is the result. 
    .catch<[U, undefined]>((err: U) => {
      if (errorExt) {
        Object.assign(err, errorExt);
      }

      return [err, undefined]; //  Execution failed and returned the array 1 Items are error messages, and the 2 Items are  undefined
    });
}

export default to;

There needs to be a pre-knowledge point here: await is waiting for the return value of an Promise.

Normally, the await command is followed by an Promise object that returns the result. If it is not an Promise object, the corresponding value is directly returned.

Therefore, we only need to use the characteristics of Promise to return different arrays in promise. then and promise. catch, where the first item of the returned array in fulfilled is null, and the second item is the result. rejected, the first item in the returned array is an error message, and the second item is undefined. When using, judge whether the first item is empty, and you can know whether there is an error. The specific use is as follows:


import to from 'await-to-js';
// If you use CommonJS (i.e NodeJS environment), it should be:
// const to = require('await-to-js').default;

async function asyncTaskWithCb(cb) {
     let err, user, savedTask, notification;

     [ err, user ] = await to(UserModel.findById(1));
     if(!user) return cb('No user found');

     [ err, savedTask ] = await to(TaskModel({userId: user.id, name: 'Demo Task'}));
     if(err) return cb('Error occurred while saving task');

    if(user.notificationsEnabled) {
       [ err ] = await to(NotificationService.sendNotification(user.id, 'Task Created'));
       if(err) return cb('Error while sending notification');
    }

    if(savedTask.assignedUser.id !== user.id) {
       [ err, notification ] = await to(NotificationService.sendNotification(savedTask.assignedUser.id, 'Task was created for you'));
       if(err) return cb('Error while sending notification');
    }

    cb(null, savedTask);
}

Summary

Adding error handling to async await is considered necessary, but the solution is not just try... catch. Using the features of async await and Promise, we can handle the errors of async await more gracefully.

Summarize


Related articles: