Utilizing singleton pattern to add custom error-handling logic to promise chains.

Have you ever been in a situation where you want to add error handling to an application containing a big promise chain and you cant figure a proper way to do it? Then this article is a must-read because here we will explain how we can overcome this problem using a fundamental design pattern, the singleton. We will explain how to give our application users, specific feedback about the failure which happened to a sequence of asynchronous actions.

First, lets explain the issue we are facing. Lets suppose we want to make some subsequent API calls from a client application (either a browser or a Node.js application). Assuming you already know how promises work we would write something like the following:

httpClient.get(‘/resource_url_1’)
.then(res => httpClient.post(‘/resource_url_2’ , { key: res.prop}))
.then(res => httpClient.post(‘/resource_url_3’ , { key: res.prop}))
.then(res => httpClient.post(‘/resource_url_4’ , { key: res.prop}))
.then(res => httpClient.post(‘/resource_url_5’ , { key: res.prop}))
.catch(err => console.log(‘Error trying to call the APIs’))

In the example above, we called 5 different resource urls and every request except for the first one was depending on the previous request. Also, we wrote a catch clause in order to log any error that may occur when hitting those APIs. Everything is good so far. Our project is working perfectly fine and suddenly one day our manager decides that we have to add error handling to every API call. The first solution that comes to our minds is to add a catch clause after every call like the following:

httpClient.get(‘/resource_url_1’)
.catch(err => console.log(‘Error trying to call /resource_url_1’))
.then(res => httpClient.post(‘/resource_url_2’ , {
key: res.prop
}))
.catch(err => console.log(‘Error trying to call /resource_url_2’))
.then(res => httpClient.post(‘/resource_url_3’ , {
key: res.prop
}))
.catch(err => console.log(‘Error trying to call /resource_url_3’))
.then(res => httpClient.post(‘/resource_url_4’ , {
key: res.prop
}))
.catch(err => console.log(‘Error trying to call /resource_url_4’))
.then(res => httpClient.post(‘/resource_url_5’ , {
key: res.prop
}))
.catch(err => console.log(‘Error trying to call /resource_url_5’))

We can optimize the chain above but this isn’t the purpose of this article.
A different and intresting approach is represented here:
https://medium.com/@arthurxavier/error-handling-in-long-promise-chains-155f610b5bc6

In fact, this is a working solution but let me ask you something. Before asking though, note that when a catch clause doesn’t return anything, a «null» value is returned implicitly. So, back to the question. What happens when the API with url ‘/resource_url_3’ is not working as expected? Let me help you with that. When this API is not working as expected the 3rd catch clause will be called. Considering our previous note, since a null value is returned the 3rd «then» clause will be invoked with «res» being a null value. This breaks our chain and of course is not the intended behavior. Now, how are we fixing this?

The most obvious solution is to create our own custom type of errors and check in every catch clause if such an error exists. Although, doing that will result in a huge switch of if statement inside every catch clause like the following:

httpClient.get(‘/resource_url_1’)
.catch(err => {
console.log(‘Error trying to call /resource_url_1’);
throw new CustomError1();
})
.then(res => httpClient.post(‘/resource_url_2’ , {
key: res.prop
}))
.catch(err => {
if(err.name === “CustomError1”)
throw err;
console.log(‘Error trying to call /resource_url_2’);
throw new CustomError2();
})
.then(res => httpClient.post(‘/resource_url_3’ , {
key: res.prop
}))
.catch(err => {
if(err.name === “CustomError1” || err.name === “CustomError2”)
throw err;
console.log(‘Error trying to call /resource_url_3’);
throw new CustomError3();
})
.then(res => httpClient.post(‘/resource_url_4’ , {
key: res.prop
}))
.catch(err => {
if(err.name === “CustomError1” || err.name === “CustomError2” || err.name === “CustomError3”)
throw err;
console.log(‘Error trying to call /resource_url_4’);
throw new CustomError4();
})
.then(res => httpClient.post(‘/resource_url_5’ , {
key: res.prop
}))
.catch(err => {
if(err.name === “CustomError1” || err.name === “CustomError2” || err.name === “CustomError3” || err.name === “CustomError4”)
throw err;
console.log(‘There was an error trying to call /resource_url_2’);
throw new CustomError5();
})

And the thing goes on… That’s a mess.

Singleton to the Rescue!

Now the point of the whole article. For those who are not aware of the singleton design pattern, we use it when we actually want to have only one instance of a class which is persistent. Let me show you an example so that you can understand better:

// Utilizing the singleton pattern in order to keep the first error that occurs
class ErrorSingleton {
    // A variable which stores the singleton object. Initially,
    // the variable acts like a placeholder
    private static singleton: ErrorSingleton;
    public error: Error;
    // private constructor so that no instance is created
    private constructor(err) {
        this.error = err;
    }
    // This is how we create a singleton object
    public static instance(err: Error): ErrorSingleton {
        // check if an instance of the class is already created
        if (this.singleton == null) {
            // If not created create an instance of the class
            // store the instance in the variable
            this.singleton = new ErrorSingleton(err);
        }
        // return the singleton object
        return this.singleton;
    }
}

The above segment of code is an actual implementation of the singleton pattern. In order to get an instance of the singleton, we just have to invoke the static «instance» method of the ErrorSingleton class. Notice the parameter (an error) that this method receives and also that everything except for the «error» property is private. Using this code will allow us to store the first error that happens in a chain of promises and discard the rest, since we are only creating the instance once. The rest of the time we return just the already existing instance.

Having a class like so, allows us to keep track of only the first error that occurred during the sequence of those asynchronous actions and actually force the promise chain to “skip” the rest of the catch clauses because they will re-throw the same error if any has occurred before.

Lets create a helper function now in order to help us achieve the intended behavior.

// Using the above singleton to generate promise rejections
function chainPromiseError(err) {
return () => Promise.reject(ErrorSingleton.instance(err);
}

Having those two things ready we can start chaining our promises and do error handling like this:

httpClient.get(‘/resource_url_1’)
.catch(err => chainPromiseError(new Error(‘Error trying to call /resource_url_1’)))
.then(res => httpClient.post(‘/resource_url_2’ , {
key: res.prop
}))
.catch(err => chainPromiseError(new Error(‘Error trying to call /resource_url_2’)))
.then(res => httpClient.post(‘/resource_url_3’ , {
key: res.prop
}))
.catch(err => chainPromiseError(new Error(‘Error trying to call /resource_url_3’)))
.then(res => httpClient.post(‘/resource_url_4’ , {
key: res.prop
}))
.catch(err => chainPromiseError(new Error(‘Error trying to call /resource_url_4’)))
.then(res => httpClient.post(‘/resource_url_5’ , {
key: res.prop
}))
.catch(err => chainPromiseError(new Error(‘Error trying to call /resource_url_5’)))
.catch((errorSingleton: ErrorSingleton) => {
// do whatever is needed with errorSingleton.error here. Like:
alert(errorSingleton.error);
})

Looks like really clean and well organized, flexible and pretty neat solution as far as asynchronous error handling is concerned and its especially useful when you want to show custom error messages to your users as feedback.

Hope you enjoyed the article and that it will be useful to you.

Tsaganos Apostolos - Software Engineer