Anjan Dutta

Promise in Javascript an introduction for beginners

Promise in Javascript an introduction for beginners

In my last article, I have written about the most important topics a javascript developer should know before going for an interview.

This article is the continuation of that series and here, I am going to discuss another important feature of ES6, the promise in javascript.

A promise is an object that helps in managing the asynchronous code execution in javascript.

A promise is immutable and it ensures to always return a value.

Syntax

var p = new Promise(function(resolve, reject){
//do something
});

Parameters

A promise constructor takes a function as a parameter. The parameter function accepts two more parameters, resolve and reject.

Resolve and reject are two executor functions. These functions are called to indicate the end of a promise.

If the desired operation is performed without any error then the resolve method is called. And, if any error occurs, then the reject method is called. We can pass a string, an array or an object as a parameter of these functions.

So that the consumers of that promise will receive the result accordingly.

var a=10;
var p = new Promise(function(resolve, reject) {
if ( a === 10) {
resolve('Result matched.');
} else {
reject('Result mismatched.');
}
});

Before ES6 promise

We used callback functions for this purpose. Look at the below example, I am converting the promise we used in the previous section to a callback based solution.

function callback(SuccessMsg, ErrorMsg) {
if (SuccessMsg) {
console.log(SuccessMsg);
} else if (ErrorMsg) {
console.log(ErrorMsg);
} else {
console.log('Unknown status');
}
}
function isMatch(a, cb) => {
if (a === 10) {
cb('Result matched.');
} else {
cb(null, 'Result mismatched.');
}
}
isMatch(2, callback); //Result mismatched.

This was a very simple example using a single callback function. Now, let suppose we have multiple callback functions that depend upon each other like below:

var a = callback1();
var b = callback2(a);
var c = callback3(b);

Here, if any of these function body include setTimeout or an ajax call, then the code will behave asynchronously.

And then, to maintain that order we have to restructure the code like below:

callback1(params) {
...........
//code goes here
...........
callback2(params) {
............
//code goes here
............
callback3(params) {
............
//code goes here
............
}
}
}

This way, we can still keep the code execution synchronous but look at the code! With increasing lines, the logic becomes unreadable and dirty.

This structure is called, The Callback Hell, where we often end up writing a double amount of code while handling success and failure cases.

States of a promise in Javascript

A promise can be in any of the four cases at any instance of time.

Fulfilled: This state indicates that the promise succeeded.

Rejected: The state to indicate that the promise has failed.

Pending: The promise is still running and it hasn’t fulfilled or rejected yet.

Settled: When a promise is fulfilled or rejected.

Consume a promise

A promise can be consumed using any of these three methods: then(), catch() and finally().

then(): This method consumes the result of a resolved or rejected promise. then() can take up to two parameter functions.

The first function gets initiated if the promise resolves. Eventually, the second function handles a rejected promise.

See the below example:

var a=10;
var p = new Promise(function(resolve, reject) {
if ( a === 10) {
resolve('Result matched.');
} else {
reject('Result mismatched.');
}
});
p.then(function(resolveMsg) {
console.log(resolveMsg); //<- above code resolved
}, function(rejectMsg) {
console.log(rejectMsg);
});

catch(): This method handles the result of a rejected promise or if an error occurs inside a promise.

See below example:

var t = 10;
var p = new Promise(function(resolve, reject) {
throw new Error('can\'t handle this');
if (t===10) {
resolve('resolved');
} else {
reject('rejected');
}
});
p.then((res)=>{
console.log(res);
}).catch((err)=>{
console.log(err.message); // can't handle this
});

finally(): This method is called after a promise settles. That means this method will be called after a promise gets resolved or rejected.

See the below example:

var t = 10;
var p = new Promise(function(resolve, reject) {
if (t===1) {
resolve('resolved');
} else {
reject('rejected');
}
});
p.then((res)=>{
console.log(res);
}).catch((err)=>{
console.log(err.message);
}).finally(()=>{
console.log('Promise setteled');
// regardless of resolve or reject above line will execute
});

Both then() and catch() can return a result which is again consumable by another then() or catch(). Hence, as a result, both of these methods are chain able. Means, we can use multiple then() or catch() in a series to perform asynchronous operation.

Here is an example of promise chaining:

var t = Math.random()*10;
t = Math.floor(t);
var p = new Promise(function(resolve, reject) {
if (t%2 === 0) {
resolve(t);
} else {
reject(t);
}
});
p.then((res)=>{
console.log('Even number generated');
return res;
}).then((res)=>{
console.log('The number is '+ res);
}).catch((err)=>{
console.log('Odd number generated '+ err);
});
// Even number generated
// The number is 4

This code is much more structured than the callback approach.

Promise.race() & Promise.all()

These two functions can take an array of promises as a parameter.

race() method returns a promise as soon as any of the promises from the array gets settled. Means, it returns a promise as soon as any of the subsequent promises gets resolved or rejected.

var p1 = new Promise(function(resolve, reject) {
setTimeout(resolve, 1500, 'Resolved p1');
});
var p2 = new Promise(function(resolve, reject) {
setTimeout(resolve, 1000, 'Resolved p2');
});
Promise.race([p1, p2]).then(function(res) {
console.log(res);
// The fasterone, p2 logs the result
});

Similarly, all() returns a promise, once all the promises in the parameter list resolve or when the first rejection appears.

var p1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('p1'), 100);
});
var p2 = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('p2')), 200);
});
var p3 = new Promise((resolve, reject) => {
setTimeout(() => resolve('p3'), 300);
});
Promise.all([p1, p2, p3])
.then(res => {
console.log(res);
})
.catch(error => {
console.log(error.message)
});
// p2 will be logged as soon as the first promise rejects.

Conclusion

A promise makes code readable and clutters free. It makes error handling very easy. In ES7 there are two new identifiers async and await. I will discuss the advantages and disadvantages of using promise with those and without those.

Subscribe to my blog for future post notifications.