ES6 Promises: A better way of handling callbacks

Write better manageable code using promises

Yogesh Chavan
Jan 15 · 6 min read

ES6 introduced promises as a native implementation. Before ES6 we were using callbacks to handle asynchronous operations.

Let’s understand what is callback and what problem related to callback is solved by promises.
Consider, We have list of posts and their respective comments.

const posts = [
{ post_id: 1, post_title: 'First Post' },
{ post_id: 2, post_title: 'Second Post' },
{ post_id: 3, post_title: 'Third Post' },
];

const comments = [
{ post_id: 2, comment: 'Great Post!'},
{ post_id: 2, comment: 'Nice Post!'},
{ post_id: 3, comment: 'Awesome Post!'},
];

We will write a function to get the post by passing post id and if the post is found we will get the comments related to that post

const getPost = (id, callback) => {
const post = posts.find( post => post.post_id === id);
if(post) {
callback(null, post);
} else {
callback("No such post", undefined);
}
};
const getComments = (post_id, callback) => {
const result = comments.filter( comment => comment.post_id === post_id);
if(result) {
callback(null, result);
} else {
callback("No comments found", undefined);
}
}

In the above getPost and getComments functions, if there is error we will pass it as the first argument and if we got the result we will call the callback function and pass the result as second argument.

If you are familiar with Nodejs, then you will know that, this is a very common practice used in every Nodejs callback function.

Now let’s use those functions

getPost(2, (error, post) => {
if(error) {
return console.log(error);
}
console.log('Post:', post);
getComments(post.post_id, (error, comments) => {
if(error) {
return console.log(error);
}
console.log('Comments:', comments);
});
});

After execution of the above code, you will see following output

Demo: https://codepen.io/myogeshchavan97/pen/PoweVgR?editors=0011#0

As you can see, we have getComments function nested inside getPost callback. Imagine if we also want to find the likes of comment then that will also get nested inside getComments callback so creating more nesting which will make code difficult to understand.

This nesting of callback is known as callback hell.

Also you can see that, the error handling condition gets repeated which creates a duplicate code which is not good.

So to fix this problem promises were introduced.

To create a new promise we use the Promise constructor

const promise = new Promise((resolve, reject) => {});

The Promise constructor accepts a function as the first parameter.

The resolve and reject are functions which automatically gets passed to the function.

Promise goes through three states.

a) Pending
b) Fulfilled
c) Rejected

When we create a promise, it’s in pending state, and when we call the resolve function, it goes in fulfilled state and if we call reject it will go in rejected state.

We perform the asynchronous operation inside the function passed to Promise constructor and when we get the response of asynchronous operation and the response is ok then we call resolve function and if there is some error then we call the reject function.

const resolvedPromise = () => {
return new Promise((resolve, reject) => {
resolve('worked!');
});
};

To access the value of resolved promise, we need to attach the then handler which will get called when promise is resolved.

resolvedPromise().then((result) => {
console.log(result); // worked!
});

When the promise gets rejected, catch handler will get called

const rejectedPromise = () => {
return new Promise((resolve, reject) => {
reject('something went wrong!');
});
};

rejectedPromise().catch((error) => {
console.log('Error', error); // Error something went wrong!
});

Note that, we can pass only a single value to resolve and reject function.

Take a look at below code

const multiply = number => {
if(number > 0) {
return number * number;
} else {
return "Error while multiplying";
}
};

const getPromise = value => {
return new Promise((resolve, reject) => {
const result = multiply(value);
if(typeof result === "number") {
resolve(result);
} else {
reject(result)
}
});
};

getPromise(4)
.then(result => console.log(result)) // 16
.catch(error => console.log(error));

getPromise(-5)
.then(result => console.log(result))
.catch(error => console.log(error)); // Error while multiplying

Here, in getPromise function we are passing some value. If the value is a number then we return the multiplication of number with itself otherwise returns an error.

Demo: https://codepen.io/myogeshchavan97/pen/PowaYBO?editors=0011

We can also attach multiple then handlers

getPromise(4)
.then(result => {
return getPromise(result);
})
.then(output => console.log('result', output))
.catch(error => console.log('error', error));

The value returned from first then call will be passed to second then call and so on.
This way of attaching multiple then calls is known as promise chaining.

Demo: https://codepen.io/myogeshchavan97/pen/eYmKYZN?editors=0011

If any one of the promise in promise chain gets rejected, the next promise in the chain will not be executed, instead the execution stops there and catch handler will be executed.

const multiply = number => {
if(number > 0) {
return number * number;
} else {
return "Error while multiplying";
}
};
const getPromise = value => {
return new Promise((resolve, reject) => {
const result = multiply(value);
if(typeof result === "number") {
resolve(result);
} else {
reject(result)
}
});
};
getPromise(4)
.then(result => {
console.log('first');
return getPromise(result);
})
.then(result => {
console.log('second');
return getPromise(-5); // passing negative value will call reject
})
.then(result => { // this will not be executed
console.log('third');
return getPromise(2);
})
.then(output => console.log('last:', output))
.catch(error => console.log('error:', error));

Demo: https://codepen.io/myogeshchavan97/pen/rNaKNWx?editors=0011

As you can see here, only the first and second then block is executed and third is skipped as the promise gets rejected because of the negative value check in multiply function.

So now, you have a good understanding of promises and promise chaining, let’s see how we can fix the callback hell problem for the first post and comment example using promise chaining.

const posts = [
{ post_id: 1, post_title: 'First Post' },
{ post_id: 2, post_title: 'Second Post' },
{ post_id: 3, post_title: 'Third Post' },
];
const comments = [
{ post_id: 2, comment_id: 1, comment: 'Great Post!'},
{ post_id: 2, comment_id: 2, comment: 'Nice Post!'},
{ post_id: 3, comment_id: 3, comment: 'Awesome Post!'},
];
const getPost = (id) => {
return new Promise((resolve, reject) => {
const post = posts.find( post => post.post_id === id);
if(post) {
resolve(post);
} else {
reject("No such post");
}
});
};
const getComments = (post_id) => {
return new Promise((resolve, reject) => {
const result = comments.filter( comment => comment.post_id === post_id);
if(result) {
resolve(result);
} else {
reject("No comments found");
}
});
}
getPost(2)
.then(post => {
console.log('Post:', post);
return post; // return the post to next then call to acess post_id
})
.then(post => getComments(post.post_id))
.then(comments => console.log('Comments:', comments))
.catch(error => console.log(error));

Demo: https://codepen.io/myogeshchavan97/pen/YzPvzVe?editors=0011

If you compare the code of callback and promise, you can see the difference as shown below

Here, we have changed callback function call to resolve or reject depending on response and used promise chaining to avoid the nesting of function calls. We also avoided duplicate error condition check using just single catch handler.

That’s it for today. Hope you learned something new today.

Subscribe to get weekly updates directly in your inbox https://subscribe-user.herokuapp.com/

JavaScript in Plain English

Learn the web's most important programming language.

Yogesh Chavan

Written by

Full Stack Developer | Javascript | React | Nodejs. Subscribe to get weekly updates directly in your inbox https://subscribe-user.herokuapp.com/

JavaScript in Plain English

Learn the web's most important programming language.

More From Medium

More from JavaScript in Plain English

More from JavaScript in Plain English

More from JavaScript in Plain English

5 Secret features of JSON.stringify()

More from JavaScript in Plain English

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