Callback vs Promises vs Async Await
Mrityunjaya Prajapati
Posted on May 8, 2021
In this blog I will explains the fundamental concepts that JavaScript relies on to handle asynchronous operations. These concepts include Callback functions, Promises and the use of Async, and Await to handle different operations in JavaScript.
Before jumping to comparison between the three, let's get a brief understanding of synchronous (blocking) and asynchronous(non-blocking).
Difference Between Sync and Async
To make it easy to understand, let’s take a real life example which probably will explain the difference between asynchronous and synchronous.
Imagine we go to a restaurant, a waiter comes to a table, takes your order and gives it to the kitchen. Lets divided whole process into the steps
- waiter takes order from table 1
- waiter inform chef for the dishes
- serve dishes to table 2 OR take order from table 2
The table has to wait for the chef to cook one meal before they serve another table. This is what we called asynchronous or non-blocking architecture. Here the waiter is like a thread allocated to handle requests. So a single thread is used to handle multiple requests.
In contrast to non-blocking or asynchronous architecture, we have blocking or synchronous architecture. Let's see how that works. So back to the restaurant example, imagine you go to another restaurant and in this restaurant, a waiter is allocated to you. He takes your order and gives it to the kitchen. Now he is sitting in the kitchen waiting for the chef to prepare your meal and this time he is not doing anything else he is just waiting for he is not going to take any order from another table until your meal is ready. This is what we called synchronous or blocking architecture.
Now, the first restaurant example represents an asynchronous process because you did not have to wait, the waiter takes the order from one table and goes to the next table to take the order. While the second example restaurant represents a synchronous operation because you had to wait until the resource (waiter in this case) can proceed with you. This is the single, most fundamental difference between sync and async processes.
There are different ways to handle the async code in JavaScript. Those are callbacks, promises, and async/await.
Callbacks:
In JavaScript, functions are objects, so we can pass objects to functions as parameters.
Let's take an example of callback function:
function printString(){
console.log("Jay");
setTimeout(function() { console.log("Jeevan"); }, 500);
console.log("Joy")
}
printString();
If that were sync code, we would have encountered the following output.
Jay
Jeevan
Joy
But the setTimeout is an async function then the output of the above code will be:
Jay
Joy
Jeevan
There is a built-in method in JavaScript called “setTimeout”, which calls a function after a given period of time (in milliseconds).
In other words, the message function is being called after something happened (after 5 seconds passed for this example), but not before.
Promises:
A promise in JavaScript you can consider as a promise in real life. When we make a promise in real life, it means we are going to do something in the future because promises can only be made for the future.
A promise has two possible outcomes: either you will keep your promise or not.
Same fundamentals of promises applies in JavaScript. When we define a promise in JavaScript, it will be resolved when the time comes, or it will get rejected.
A promise is used to handle the asynchronous result of an operation. JavaScript is designed to not wait for an asynchronous block of code to completely execute before other synchronous parts of the code can run. With Promises, we can defer the execution of a code block until an async request is completed. This way, other operations can keep running without interruption.
States of Promises:
First of all, a Promise is an object. There are 3 states of the Promise object:
- Pending: Initial State, before the Promise succeeds or fails.
- Resolved: Completed Promise
- Rejected: Failed Promise, throw an error
For example, when we request data from the server by using a Promise, it will be in pending state until we receive our data.
If we get the information from the server, then Promise will be resolved successfully but if we don’t get the information, then the Promise will be in the rejected state.
Creating a Promise:
Firstly, we use a constructor to create a Promise object. The promise has two parameters, one for success (resolve) and one for fail (reject):
const myFirstPromise = new Promise((resolve, reject) => {
const condition = true;
if(condition) {
setTimeout(function(){
resolve("Promise is resolved!");
}, 500);
} else {
reject('Promise is rejected!');
}
});
In the above Promise If Condition is true, resolve the promise returning the “Promise is resolved ”, else return an error “Promise is rejected”. Now we have created our first Promise, Now let's use it.
Using Promise:
To use the above create Promise we use then()
for resolve and catch()
for reject.
myFirstPromise
.then((successMsg) => {
console.log(successMsg);
})
.catch((errorMsg) => {
console.log(errorMsg);
});
let's take this a step further:
const demoPromise= function() {
myFirstPromise
.then((successMsg) => {
console.log("Success:" + successMsg);
})
.catch((errorMsg) => {
console.log("Error:" + errorMsg);
})
}
demoPromise();
In our created promise condition is “true” and we call demoPromise() then our console logs read:
Success: Promise is resolved!
So if the promise gets rejected, it will jump to the catch()
method and this time we will see a different message on the console.
Error: Promise is rejected!
Async/Await:
Await is basically syntactic sugar for Promises. It makes your asynchronous code look more like synchronous/procedural code, which is easier for humans to understand.
Syntax of Async and Await:
async function printMyAsync(){
await printString("one")
await printString("two")
await printString("three")
}
You can see that we use the “async” keyword for the wrapper function printMyAsync. This lets JavaScript know that we are using async/await syntax, and this is necessary also if you want to use Await. We can say that await is only used with an async function.
The await keyword is used in an async function to ensure that all promises returned in the async function are synchronized, ie. they wait for each other. Await eliminates the use of callbacks in .then() and .catch(). In using async and await, async is prepended when returning a promise, await is prepended when calling a promise. try and catch are also used to get the rejection value of an async function.
Let's take an example to understand the Async and Await with our demoPromise:
const helloPromise = function() {
return new Promise(function(resolve, reject) {
const message = "Hi, How are you!";
resolve(message)
});
}
async function demoPromise() {
try {
let message = await helloPromise();
console.log(message);
}
catch(error){
console.log("Error:" + error.message);
}
}
demoPromise();
Conclusion
Based on our use case we can prefer any one of this approach. Since async/await is wrapped on top of Promise, all of the Promise related functionalities are supported within it. So when comparing callback with Promise, Promise has move advantages than callback. Listing out few of them;
- Single Error Propagation using catch block
- Overcome callback hell using Promise Chaining or async/await/
- Implement parallel processing using
Promise.all()
. - Promise supports few other static methods like
(race(), allSettled() and any())
which shall be very usefully on need basic.
Hope this story would have helped you in refreshing the asynchronous handling concepts in Javascript. Please feel free to share your comments, suggestions or queries.
Posted on May 8, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.