DEV Community

grangus
grangus

Posted on

No Try/Catch! A JavaScript library to make your asynchronous code cleaner!

If you write apps using Node.JS, you've probably ended up in then-catch hell.

This code below works, but when you have to do large operations that require you to chain many promises together, it can get super repetitive and hard to read and maintain.

//then-catch hell
somePromise(arg1, arg2, arg3)
 .then((result) => {
   anotherPromise(arg1, arg2, arg3)
     .then((anotherResult) => {
       andAnotherPromise(arg1, arg2, arg3)
         .then((andAnotherResult) => {})
         .catch((error) => {
           console.error(error);
         });
     })
     .catch((error) => {
       console.error(error);
     });
 })
 .catch((error) => {
   console.error(error);
 });

Enter fullscreen mode Exit fullscreen mode

This is where async/await can come in handy, but if you've ever used the await keyword on a promise, you probably needed to catch errors and ended up with some ugly code like this.

//try-catch tower of terror
let result;

try {
 result = await somePromise(arg1, arg2, arg3);
} catch (error) {
 return console.error(error);
}

let anotherResult;

try {
 anotherResult = await anotherPromise(arg1, arg2, arg3);
} catch (error) {
 return console.error(error);
}
Enter fullscreen mode Exit fullscreen mode

Of course, you could always wrap all your operations in a single try-catch (example below), but you would lose granularity unless you are using your own libraries and writing unique error codes for everything that could go wrong (not fun). The lack of granularity in the example below would make it very hard to debug if something goes wrong.

try {
 let result = await somePromise(arg1, arg2, arg3);
 let anotherResult = await anotherPromise(arg1, arg2, arg3);
} catch (error) {
 return console.error(error);
}
Enter fullscreen mode Exit fullscreen mode

Now, I present to you no-try-catch! The solution to the hell you have been going through.

All you need to do is wrap your promises and check if the result has an error property. It also supports TypeScript, so the data property will be typed with the promise result.

import wrapper from "no-try-catch";
//or const wrapper = require("no-try-catch");

let result = wrapper(somePromise, [arg1, arg2, arg3]); //returns {error: Error, data: null} or {error: null, data: RESULT_FROM_PROMISE}

if (result.error) return console.error(result.error);

console.log(result.data);
Enter fullscreen mode Exit fullscreen mode

You can install the package from NPM or review the source code on Github.

https://www.npmjs.com/package/no-try-catch
https://github.com/grangus/no-try-catch

If you find any bugs (you probably will), please open an issue on Github.

Top comments (2)

 
realgrangus profile image
grangus

Never would have thought of that TBH. I really like the secure promise wrapper idea. I did originally plan on using a tuple instead of an object, but TypeScript loved to scream "data is possibly undefined or null". My solution ended up using an object instead. Thanks for the enlightenment. I'll be sure to hit you up with more programming questions in the future 🤠

Collapse
 
realgrangus profile image
grangus

This is nice, but the issue I see is with error granularity.

Promise.all([
    somePromise(arg1, arg2, arg3),
    anotherPromise(arg1, arg2, arg3),
    andAnotherPromise(arg1, arg2, arg3)
])
    .then(([result, anotherResult, andAnotherResult]) => {})
    .catch(console.error);
Enter fullscreen mode Exit fullscreen mode

AFAIK this doesn't specify where the error came from. So, if you get one of those super generic errors for some reason, pinpointing which promise threw the error in the first place would be significantly harder.

Say you are calling an async function from some external library. Something happens and the lib throws an error like

Cannot read property "x" of undefined"

. You know the error originated from one of those chained promises, but you don't know which one threw the error.