JavaScript Promises in 5 Concepts
- syntax
- chaining
- handling multiple promises together
- async-await
- pre-resolved promises
Introduction
Javascript is a single-threaded programming language supporting asynchronous execution to fulfill the needs of concurrent execution without blocking the main thread. Javascript promises are a great way to handle async execution.
Promises hold the eventual result of an async operation. For example, if we hit some REST API, we can use promises to get the eventual result of that API.
A promise can be in any of below mentioned three stages:
- Pending: Waiting for result
- Fulfilled: Promise results in success
- Rejected: Promise result in failure
Concept 1: How to create a promise & consume it
Let’s take an async function as given below
function async_fn() {
setTimeout(function(){
console.log("executed");
}, 1000);
}
Now if we want to return something from async_fn, we have two options:
- Use a callback function
- Use promise
The disadvantage of the first approach is that it leads to a callback hell problem.
Now, let’s see how this issue can be solved using promises. To create a promise we use below given syntax
new Promise((resolve, reject)=>{
if(success){
resolve("success");
} else {
reject("failed");
}
});
Example:
function timer() {
return new Promise((resolve, reject)=> {
setTimeout(()=>{
resolve("Timeout");
},2000);
});
}
Promise provides three methods, to consume promise and handle the result
- then: if promise results in success,
then
method callback is called - catch: if promise fails,
catch
method callback is called - finally: method callback called when promise fulfilled either in success or failure. Can be used for common task such as hiding the loader etc.
Now, let’s see how to consume a promise in the following example:
timer().then((response)=>{
// function returned in success
console.log(response);
}).catch((error)=>{
// function returned in error
console.log(error);
}).finally(()=>{
// always called
console.log("Promise completed");
});
Concept 2: Chaining a Promise
then
method of a promise returns a new settled promise that can be used for further chaining. Assume a scenario where you want to use multiple callbacks for a given promise and process the result one by one in pipeline format i.e result of one callback to be used in other. This can be implemented using the chaining technique.
Chaining can be implemented in two ways.
- Using individual error handler for each success callback :
then
method takes two arguments one for success callback and the other is for the failure callback. As given in the below example:
timer().then(successHandler1, failureHandler1).
.then(successHandler2, failureHandler2)
.then(successHandler3, failuerHandler3)
.then(successHandler4, failureHandler4)
Note: Errors thrown in the previous successHandler
will get passed to next failureHandler
callback e.g. error thrown in successHandler1 will be captured in failureHandler2 and subsequent error handlers.
2. Using common error Handler: As the failureHandler
callback is optional in the then
method, we can pass only successHandler
in then
method and use a catch
block for common error handling as given in below example
timer().then(successHandler1)
.then(successHandler2)
.then(successHandler3)
.catch(errorHandler)
See the example code below :

As you can see in the above example, how we can pipeline value parameter through different callbacks and increment their value as going down.
If we
throw error
from any ofthen
callback then for the next & followingthen
methodserror
callback will get executed and finally parsederror
will be passed to finalcatch
block
Concept 3: Handling multiple promises together
Sometimes it requires to handle two or more promise and execute handler when these promises get resolved. Now there are two ways to achieve this:
- Wait for all promises to resolve: to achieve this Promise object provide
all
method that takes one or more promises as an array, and returns a promise that gets resolved when all of them result in success or any of them fail. See the syntax below
Promise.all([promise1, promise2, promise3])
.then(response => {
// response is array contains result of promises passed
}).catch(error => {
// only first error will be passed i.e. if two promise result in error then first error will be passed to error callback
})
All promises will get executed disregard of their results in success or failure for example, if
promise2
results in failure then,promise3
will be executed butcatch
handler will called immediately and error ofpromise2
will be passed tocatch
block andthen
block will never get executed
See the example program below :

2. Wait for at least one promise (from a list of promises) to resolve: If we want to resolve parent promise as soon as any of the child promises resolves, we can use the race
method available in the Promise
object. See the syntax below :
Promise.race([promise1, promise2, promise3])
.then(response => {
// response contains result of first promise resolved
}).catch(error => {
// error contains error of first promise resolved
})
See the example program below :

Concept 4: Async & Await
Async-await is a way that allows you to call asynchronous functions in a synchronous manner that results in cleaner code. So let’s dive in.
As of now, we know that async functions are functions that go to pending state on call & wait for some result and resolve to either success or failure depends on the result of execution. So now assume you have to handle two or more such functions and the result of the first function needs to be passed to the second function. One way to handle this is by using the second function inside then
callback of the first function but it will make code more complicated to understand. Here comes the async-await to rescue. Let’s see how.
Let’s assume we have two async functions, getManagerByEmployeeId, getManagerNameById.
getManagerByEmployeeId: It takes employeeId as input and returns reporting managerId.
getManagerNameById: It takes managerId as input and returns manager name.
Now our task is to get the name of the reporting manager of the given employee id, in short, to implement getManagerName function in the below code.
const EmployeeIDManagerIdMap = {
"AA234": "0AA316",
"BBCD5":"4AA354"
};const ManagerIdManagerNameMap = {
"0AA316":"John Doe",
"4AA354":"Ravindram S"
};function getManagerByEmployeeId(employeeId) {
return new Promise((resolve, reject)=> {
setTimeout(()=>{
if(EmployeeIdManagerIdMap[employeeId]) {
resolve(EmployeeIdManagerIdMap[employeeId]);
} else {
reject(`Invalid employee id ${employeeId}`);
}
},2000);
});
}function getManagerNameById(managerId) {
return new Promise((resolve, reject)=> {
setTimeout(()=>{
if(ManagerIdManagerNameMap[managerId]) {
resolve(ManagerIdManagerNameMap[managerId]);
} else {
reject(`Invalid manager id ${managerId}`);
}
},2000);
});
}// get Manager Name by employeeId
function getManagerName(employeeId) {
// return manager name
}
So one way to do this is using nested promise structure like given below and convert getManagerName into a promise
function getManagerName(employeeId){
return new Promise((resolve, reject)=>{
getManagerByEmployeeId(employeeId).then(function(managerId){
getManagerNameById(managerId).then(function(managerName){
resolve(managerName);
}, function(error){
reject(error);
})
}, function(error){
reject(error);
})
})
}
As you can see this will lead to a nested code structure which is a bit difficult to understand and unnecessarily forces us to use promise in getManagerName function.
Now let’s see the async-await implementation:
async function getManagerName(employeeId){
try {
let managerId = await getManagerByEmployeeId(employeeId);
try {
let managerName = await getManagerNameById(managerId);
return managerName;
} catch(error) {
console.error("getManagerNameById promise rejected", error);
}
} catch(error){
console.error("getManagerByEmployeeId promise rejected", error);
}
}
So, as you can now see, the code has been simplified and we don’t need to use promise in getManagerName function.
What await do is deferring the execution until the called async function gets resolved, and every function containing await needs to be declared async by using the async keyword as we are doing in the above code. Also, rejected promise throws an error in function call hence can be handled using a try-catch block surrounding the async function call.
Concept 5: Using resolved promises
During unit testing, sometimes we need to mimic the operation of the actual promise and utilize that for testing. We can do so by using already resolved promises using resolve & reject methods of the Promise class.
See the example below:
// In similar way you can also use reject method
const getEmployeeData = Promise.resolve({"name":"Piyush", "age":"25", "ssn":"1324ssb"});testEmployeeSuccess = function(){
// promise is already resolved
getEmployeeData().then(function(employeeData){
var result = processData(employeeData);
if(testResult(result)){
console.log("test OK");
} else {
console.log("test failed");
}
});
}Check my GitHub Repository to see more About JavaScript Promises Related examples and exercises.https://github.com/nushkymohamed/APLICATION-FRAMEWORKS-LAB/blob/main/lab2.html
Comments
Post a Comment