Javascript Promises – Part II – Handling errors

Javascript Promises – Part II – Handling errors

In the previous article, we’ve learned what Promise is and went through some basic use cases of Promise. In this article, we will learn how to handle errors while using Promise.

If you haven’t read part I, I would recommend that you take a look at it here.

If you’ve read part I, you may have wondered when the reject function would be called and how to specify a real reject function to the Promise.

Actually, in the previous part, to make life simple, we’ve ignored the reject function. The full version of .then() should come with 2 parameter: resolve and reject function.

Reject function

To be clearer on this, let’s open our cool console tab in chrome and type the following code

var p1 = new Promise(function(resolve, reject) {
    console.log('1');
    throw 'Uh-oh!';
});

p1;

The console outputs would be like below:

Promise { [[PromiseStatus]]: "rejected", [[PromiseValue]]: "Uh-oh!" }

Ok, something have changed. p1‘s status is now "rejected" and its PromiseValue is now "Uh-oh!".
What happened was that during its execution, p1 met an error (exception) and it cannot be resolved, but was rejected. Thus its status is "rejected" and the exception is saved in the PromiseValue, so that it can be passed to the “reject function” if specified.

Now let’s try to specify the resolve function as we did in the previous part:

var p2 = p1.then(function(val) {
    console.log('2: ' + val);
    return val;
});

p2;

The console outputs would be:

Promise { [[PromiseStatus]]: "rejected", [[PromiseValue]]: "Uh-oh!" }

p2 status is also "rejected" and the PromiseValue is forwarded to p2. More over, we can see that the “resolve function” was not called since there was no console log output.

Let’s change p2 a little bit by adding a “reject function” to .then():

var p2 = p1.then(
    function(val) {
        console.log('resolve 1: ' + val);
        return val;
    },
    function(err) {
        console.log('reject 1: ' + err);
        return err;
    }
);

p2;

The console outputs would now be:

Promise { [[PromiseStatus]]: "resolved", [[PromiseValue]]: "Uh-oh!" }

p2 is now "resolved", the console logs "reject 1: Uh-oh!" and the PromiseValue is "Uh-oh!", which is because we called return err; in the reject function.
In the code above, we pass 2 params to .then(), the first one is the “resolve function” to call when p1 succeeds, the second one is the “reject function” to call when p1 fails. Since p1 is rejected, the reject function is called. There’s no error during execution of p2 so p2 status is resolved.
Now if we run the following code:

var p3 = p2.then(
    function(val) {
        console.log('resolve 2: ' + val);
        return val;
    },
    function(err) {
        console.log('reject 2: ' + err);
        return err;
    }
);

p3;

The console outputs would be as below:

Promise { [[PromiseStatus]]: "resolved", [[PromiseValue]]: "Uh-oh!" }

We can see that the “resolve function” was called and p3 is resolved .

then() and catch()

To make life even simpler and code even clearer, Promise provides us with another helpful function: catch()
Instead of writing this:

var p2 = p1.then(
    function(val) {
        console.log('resolve 1: ' + val);
        return val;
    },
    function(err) {
        console.log('reject 1: ' + err);
        return err;
    }
);

p2;

We can now write this:

var p2 = p1
    .then(function(val) {
        console.log('resolve 1: ' + val);
        return val;
    })
    .catch(function(err) {
         console.log('reject 1: ' + err);
         return err;
    })

p2;

Which will basically execute and output the same thing:

Promise { [[PromiseStatus]]: "resolved", [[PromiseValue]]: "Uh-oh!" }

Technically, the call to .catch(rejectFunction) is equal to calling .then(undefined, rejectFunction) and in the the above code, there was actually another Promise in between, right after the first .then(). So the above code is actually:

var p1_1 = p1
    .then(function(val) {
        console.log('resolve 1: ' + val);
        return val;
    });

p1_1;

var p2 = p1_1.catch(function(err) {
    console.log('reject 1: ' + err);
    return err;
})

p2;

One good thing about Promise is that if an error is not handled by a promise, it make the promise rejected and keep forwarding the error to the next promise. Therefore in the above code, p1 does not have a reject function so p1_1‘s status is rejected and the error get passed to the next promise. Since p1_1 has a reject function (specified by .catch()), p2 is resolved.

If we don’t use .catch(), and specified the reject function inside of a .then(), then we must specified the next nested layer of resolve and reject functions inside of the first reject function, and if this keeps going on, we would end up in the same callback hell as when we don’t use Promise at all.

By using .catch(), the error handling would also be much simpler when we chain promise after promise like this:

var p2 = p1
    .then(resolveFunc1)
    .then(resolveFunc2)
    .then(resolveFunc3)
    .then(resolveFunc4)
    .catch(rejectFunc);

When we write like this, if error happens during any node of the promise chain, the rejectFunc will be called.
We can also catch the error for a particular promise separately like this:

var p2 = p1
    .then(resolveFunc1)
    .then(resolveFunc2)
    .catch(rejectFunc2)
    .then(resolveFunc3)
    .then(resolveFunc4)
    .catch(rejectFunc);

In this part, you’ve learned how to handle errors using reject function and .catch(). We will discuss more on Promise chain in the next part of this blog series. That’s it for part II.

Next in series: Javascript Promises – Part III – Chaining Promise’s

Leave a Reply

Your email address will not be published. Required fields are marked *