How to avoid try/catch statements nesting/chaining in JavaScript ?

And how to make your code cleaner and look kind of sequential! With a simple, easy and efficient errors handling method.

Andréas Hanss
Nov 13 · 4 min read

💡Note: After some feedback from readers I would like to clarify some things. First the following pattern is not a drop in replacement for a classic try/catch error handling strategy. In fact, in some use cases it can even be an anti-pattern. The way this article shows this pattern is as a complementary way to handle errors where you want to take a direct action in response to an error. Finally, everything you’ll read here is subject to each person programming style and there are many others patterns available. Thank you for your kindness 🙏🏻 Now let’s continue to the article

Everyday developers are writing code that we need to check for potential errors.

“Try / Catch” statement are everywhere… and sometimes they are even nested or chained. This leads to such example:

async function anyFunction() {
try {
const result = await fetch("");
} catch (e) {
// Some thing
try {
const anotherresult = await someOtherAsync();
} catch (error) {
// some other error

You are writing try/catch blocks everywhere to prevent errors that crash your app. Sometimes overly because:

  • You want to standardize your errors management.
  • And you don’t want rely on external libraries errors format (or customize it).

You’ll concede: This is really boring, redundant and cumbersome.

But we’ll see below, we can do something thinner and very powerful with a small amount of code.

🙏🏻 Handling JavaScript errors in a cool manner

In every languages handling exceptions is pretty common.

Some language - like PHP - are able to provide errors types in catch instructions and are able to use multiple catch block. Some don’t and are a bit poor in the way to handle errors, like JavaScript.

You can quickly end in a situation where you are writing much code with much nesting or chaining, this can become very verbose and poorly maintainable.

async function anyFunction() {
try {
const result = await fetch("");
try {
const another = await fetch("");
} catch(anotherError) {
} catch (e) {
// Some other error handling
try {
const anotherResult = await someOtherAsync();
} catch (errorFromAnotherResult) {
// Some other error

💡Some language already have nice tools to deal with errors

Some languages like GOlang can return multiples values from return statements in functions. This allows to return an error (or null) plus a object with the expected value from the Promise, everything in the same statement.

The following sample is a GO example of network subscriber.

func Listen(host string, port uint16) (net.Listener, error) {
addr, addrErr := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", host, port))
if addrErr != nil {
return nil, fmt.Errorf("Listen: %s", addrErr)

listener, listenError := net.ListenTCP("tcp", addr)
if listenError != nil {
return nil, fmt.Errorf("Listen: %s", listenError)

return listener, nil

As you can see that this has “almost” the same pattern as a NodeJS callbacks, first arg is the value, second arg is the error (in fact NodeJS callback have those in the inverted order… But you get the logic).

✅ You can also handle JavaScript errors using same pattern

While JS doesn’t have multiple return statement, there is another way to implement the pattern we just seen above.

You can mimic this behavior by returning an array with 2 data, this also work in any languages that support arrays.

Alright, can you show me the code now ?

To achieve this, we will need to wrap our async code with a small utility function. This function will format the resolving and rejecting arguments to an array of two elements. This wrapping function will be called here to but feel free to give it any name.

* @param { Promise } promise
* @param { Object } improved - If you need to enhance the error.
* @return { Promise }
export function to(promise, improved){
return promise
.then((data) => [null, data])
.catch((err) => {
if (improved) {
Object.assign(err, improved);

return [err]; // which is same as [err, undefined];

This function is returning an array of two elements:

  • On the then callback (if the Promise resolved): it returns null and the data as there is no errors.
  • On the catch callback (if the Promise rejected): it returns the err that can be extended and undefined as second element as there is no data.

We can now take our original try/catch block and update it that way. What is done here is simply wrapping the someAsyncData Promise by our to function.

const [error, result] = await to(someAsyncData());if(error){
// log something and return ?
const [error2, result2] = await to(someAsyncData2());if(error2){
// do something else
} else {
// Here we are sure that result2 is defined and a valid value

Looks cool isn’t ? That’s simple as that. If the promise resolve we return an array with null and the data, otherwise we return the error and undefined.

Now you can use that sequential like syntax to handle errors in addition of your traditional try/catch blocks.

Did you know that pattern? Are you already using it? Feel free to share your feelings here. In my concern, that was game changer, even if it’s pretty simple and nothing new 😅. But be advise, this is not a drop in replacement for traditional way to deal with errors, just give it a try and build your own opinion.

Some libraries that implement kind of pattern are available here:

JavaScript in Plain English

Learn the web's most important programming language.

Andréas Hanss

Written by

👨🏻‍💻Javascript Tech leads @ Kaliop 🇫🇷 — react | react-native | modern js |🔥 Firebase —

JavaScript in Plain English

Learn the web's most important programming language.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade