Handling unexpected behavior in asynchronous JavaScript using Promise

Tirumal Sutrave
4 min readSep 19, 2020

When you are coding for sample program you might not come across the unexpected behavior. But whenever you start writing an production code, you have to account for the unexpected behavior due to incorrect data, failure in code etc. What come to your mind when you talk about handling unexpected behavior in code? If you are thinking try/catch/finally then you will definitely get what we will be discussing in this blog. If you are not aware of try/catch you can read here.

Some times it is difficult to relate asynchronous style of coding but if you relate it with your existing synchronous style of code you will easily understand this. To handle exceptions/unexpected behavior in asynchronous programming promise provides then/catch/finally callbacks. These are exactly similar to the try/catch/finally block. There is only one difference the try/catch you need to have catch or finally whenever you are using try but in promise you can use anything independently then or catch or finally. Behavior stays mostly same.

In synchronous code, try is used to write your logic where programmer expect some unexpected things to happen. Catch block you try to catch the unexpected exceptions or errors and handle it gracefully. And in finally block you write a piece of code which executed in the end no matter what. Similar to that in synchronous style, programmer writes a code which can throw an exception in then callback. Catch callback catches exception that is thrown in then callback. And finally callback will execute no matter what. Isn't that simple?

As promise is mostly used in modern asynchronous JavaScript let’s understand a bit on what I mean by asynchronous behavior in promise.

console.log("Start of sync code..");
var my_promise = new Promise(function(resolve, reject){
console.log("Promise constructor called..");
resolve(5);
});
my_promise.then(function(async_input){
console.log("Asynchronously called : " + async_input);
});
console.log("End of sync code..");
Output:
Start of sync code..
Promise constructor called..
End of sync code..
Asynchronously called : 5

If you notice in above code, promise constructor code/callback execute synchronously within the same path of execution it is in. But then callback executes in asynchronously. This is the reason why statement “Asynchronously called : 5” is called after “End of sync code..”.

Same concept applies for the promise callbacks that are chained.

console.log("Start of sync code..");
var my_promise = new Promise(function(resolve, reject){
console.log("Promise constructor called..");
resolve(5);
});
my_promise.then(function(async_input){
console.log("Asynchronous callback #1: " + async_input);
}).then(function(){
console.log("Asynchronous callback #2");
}).then(function(){
console.log("Asynchronous callback #3");
});
console.log("End of sync code..");
Output:
Start of sync code..
Promise constructor called..
End of sync code..
Asynchronous callback #1: 5
Asynchronous callback #2
Asynchronous callback #3

Now you know asynchronous behavior in Promise. Coming back to our topic, let’s see how catch and finally block executes.

console.log("Start of sync code..");
var my_promise = new Promise(function(resolve, reject){
console.log("Promise constructor called..");
resolve(5);
});
my_promise.then(function(async_input){
console.log("Asynchronous callback : " + async_input);
throw new Error("Thrown in then callback..");
}).catch(function(m){
console.log("Asynchronous catch callback.." + m);
});
console.log("End of sync code..");
Output:
Start of sync code..
Promise constructor called..
End of sync code..
Asynchronous callback : 5
Asynchronous catch callback..Error: Thrown in then callback..

Catch will only execute when some error is thrown in promise or then callback.

If error occurs in the constructor, subsequent then callback will not be called and it will directly go to catch callback. But at the same time notice that catch is executing asynchronously. This is the reason why “Asynchronous catch callback..” executes after “End of sync code..”. Let’s see below example explaining same.

console.log("Start of sync code..");
var my_promise = new Promise(function(resolve, reject){
console.log("Promise constructor called..");
throw new Error("Thrown in then callback..");
resolve(5);
});
my_promise.then(function(async_input){
console.log("Asynchronous callback : " + async_input);
}).catch(function(m){
console.log("Asynchronous catch callback.." + m);
});
console.log("End of sync code..");
Output:
Start of sync code..
Promise constructor called..
End of sync code..
Asynchronous catch callback..Error: Thrown in then callback..

If you want to execute something no matter what then instead of repeating code in every callback you can use finally block.

function doSomethingCommon(){
console.log("Do something common..");
}
console.log("Start of sync code..");
var my_promise = new Promise(function(resolve, reject){
console.log("Promise constructor called..");
throw new Error("Thrown in then callback..");
resolve(5);
});
my_promise.then(function(async_input){
console.log("Asynchronous callback : " + async_input);
//doSomethingCommon();
}).catch(function(m){
console.log("Asynchronous catch callback.." + m);
//doSomethingCommon();
}).finally(function(){
doSomethingCommon();
});
console.log("End of sync code..");
Output:
Start of sync code..
Promise constructor called..
End of sync code..
Asynchronous catch callback..Error: Thrown in then callback..
Do something common..

You can note in below program, use of finally does not make any sense but it show that finally also executes asynchronously. And finally can also be used independently.

function doSomethingCommon(){
console.log("Do something common..");
}
console.log("Start of sync code..");
var my_promise = new Promise(function(resolve, reject){
console.log("Promise constructor called..");
throw new Error("Thrown in then callback..");
resolve(5);
});
my_promise.finally(function(){
doSomethingCommon();
});
console.log("End of sync code..");
Output:
Start of sync code..
Promise constructor called..
End of sync code..
Do something common..
Uncaught (in promise) Error: Thrown in then callback..
at <anonymous>:7:11
at new Promise (<anonymous>)
at <anonymous>:5:18

If you note in above code, “Do something common..” statement is executing after execution of last line in code. But this time you will see stack trace in the end.

If then callback is used after catch callback, even after then callback that are before the catch callback throws error then callback after catch will execute.

console.log("Start of sync code..");
var my_promise = new Promise(function(resolve, reject){
console.log("Promise constructor called..");
throw new Error("Thrown in then callback..");
resolve(5);
});
my_promise.then(function(async_input){
console.log("Asynchronous callback : " + async_input);
}).catch(function(m){
console.log("Asynchronous catch callback.." + m);
}).then(function(async_input){
console.log("Then callback after catch callback executes..");
}).finally(function(){
console.log("Asynchronous finally callback..");
});
console.log("End of sync code..");
Output:
Start of sync code..
Promise constructor called..
End of sync code..
Asynchronous catch callback..Error: Thrown in then callback..
Then callback after catch callback executes..
Asynchronous finally callback..

--

--

Tirumal Sutrave

I'm a software developer by profession. I like to read tech stuff. I try to share my experiences from my learnings.