Javascript Promise & Promise APIs

Kishan Patel
6 min readNov 23, 2023

A Promise is an object representing the eventual completion or failure of an asynchronous operation.

Promises are used to handle asynchronous operations in JavaScript. They are easy to manage when dealing with multiple asynchronous operations where callbacks can create callback hell leading to unmanageable code.

Components of Promise

  • State
  • Result (the result of promise is immutable)

A promise has three states:

  • pending: the promise is still in the works
  • fulfilled: the promise resolves successfully and returns a value
  • rejected: the promise fails with an error

What does promise look like,

createOrder(cart)
.then((orderId) => {
return fetchPaymentDetails(orderId);
})
.then((paymentDetails) => {
return processPayment(paymentDetails);
})
.then((result) => {
return showSuccessMessage(result);
})

Example 1:

// producer
const examResult = new Promise((resolve, reject) => {
const res = Math.floor((Math.random() * 100) + 1);
if(res >= 50 ) {
resolve(`Pass: ${res}`)
} else {
const err = new Error(`Failed: ${res}`);
reject(err); // rejection should be an error
}
})

// consumer
examResult
.then(res => console.log(res))
.catch(err => console.error(err.message))
.finally(() => console.log('Done!'))

Example of promise

const cart = ['Shoes', 'Shirt', 'Pant'];

createOrder(cart)
.then((orderId) => {
return proceedToPayment(orderId); // keep returning promise to chain
})
.then((paymentId) => {
console.log('Payment Successfull with paymentId:', paymentId);
})
.catch((err) => { // catch error from any of the above promise
console.log(err.message);
})
.finally(() => { // finally block will always execute
console.log('End of order!');
});

function createOrder(cart) {
return new Promise((resolve, reject) => {
// Validate cart
if(cart.length >= 3) { // modify this to test error
const orderId = '12345';
resolve(orderId);
} else {
const err = new Error('Invalid cart, Order not created!');
reject(err);
}
})
};

function proceedToPayment(orderId) {
return new Promise((resolve, reject) => {
// Validate orderId
if(orderId === '12345') { // modify this to test error
const paymentId = '67890';
resolve(paymentId);
} else {
const err = new Error('Invalid orderId, Payment not processed!');
reject(err);
}
})
};

There are 4 types of promise APIs

  • Promise.all
  • Promise.allSatteled
  • Promise.race
  • Promise.any

Promise.all

The Promise.all() static method takes an iterable of promises as input and returns a single Promise.

  • It will wait for all of them to finish.
  • As soon as any promise gets rejected, Promise.all will throw the same error received from the rejected promise (p2). It will not wait for other promises to get successful/rejected.
  • That means you will get all data(if all are succeed) or error (if anyone is rejected)

Let’s understand it through example.

  • All promise succeeds
const p1 = new Promise((success, reject) => {
setTimeout(() => {
success('P1 Success.');
}, 3000)
})

const p2 = new Promise((success, reject) => {
setTimeout(() => {
success('P2 Success.');
}, 1000)
})

const p3 = new Promise((success, reject) => {
setTimeout(() => {
success('P3 Success.');
}, 2000)
})


const allPromise = Promise.all([p1, p2, p3]);

allPromise.then(res => console.log(JSON.stringify(res, null, 2)))

// Output
/*
[
"P1 Success.",
"P2 Success.",
"P3 Success."
]
*/
  • Any promise rejected
const p1 = new Promise((success, reject) => {
setTimeout(() => {
success('P1 Success.');
}, 3000)
})

const p2 = new Promise((success, reject) => {
setTimeout(() => {
success('P2 Success.');
}, 1000)
})

const p3 = new Promise((success, reject) => {
setTimeout(() => {
reject('P3 Rejected.');
}, 2000)
})


const allPromise = Promise.all([p1, p2, p3]);

allPromise
.then(res => console.log(JSON.stringify(res, null, 2)))
.catch(err => console.error(err))

// P3 Rejected.

Promise.allSatteled

Promise.allSettled() also takes an iterable of promises as input and returns a single Promise.

We can use Promise.allSettled() when we want to get results from all promises even if few/all promises are rejected.

Here, after 1s p2 gets rejected but Promise.allSetteled will still wait for other promises to be completed and it will give us a result from all promises

In case of success of all promises, the result will be [val1, val2, val3]

In case of failure of any promise, the result will be: [val1, error2, val3].

  • All succeed
const p1 = new Promise((success, reject) => {
setTimeout(() => {
success('P1 Success.');
}, 3000)
})

const p2 = new Promise((success, reject) => {
setTimeout(() => {
success('P2 Success.');
}, 1000)
})

const p3 = new Promise((success, reject) => {
setTimeout(() => {
success('P3 Success.');
}, 2000)
})

const allPromise = Promise.allSettled([p1, p2, p3]);

allPromise.then(res => console.log(JSON.stringify(res, null, 2)))



// Output

/*
[
{
"status": "fulfilled",
"value": "P1 Success."
},
{
"status": "fulfilled",
"value": "P2 Success."
},
{
"status": "fulfilled",
"value": "P3 Success."
}
]
*/
  • Few rejected
const p1 = new Promise((success, reject) => {
setTimeout(() => {
success('P1 Success.');
}, 3000)
})

const p2 = new Promise((success, reject) => {
setTimeout(() => {
success('P2 Success.');
}, 1000)
})

const p3 = new Promise((success, reject) => {
setTimeout(() => {
reject('P3 Rejected.');
}, 2000)
})

const allPromise = Promise.allSettled([p1, p2, p3]);

allPromise.then(res => console.log(JSON.stringify(res, null, 2)))



// Output

/*
[
{
"status": "fulfilled",
"value": "P1 Success."
},
{
"status": "fulfilled",
"value": "P2 Success."
},
{
"status": "rejected",
"reason": "P3 Rejected."
}
]
*/

Promise.race

The Promise.race() takes an iterable of promises as input and returns a single Promise. This returned promise settles with the eventual state of the first promise that settles.

This means, the first finisher will be returned, it does not matter if it succeeds or is rejected.

  • The first finisher succeed
const p1 = new Promise((success, reject) => {
setTimeout(() => {
success('P1 Success.');
}, 3000)
})

const p2 = new Promise((success, reject) => {
setTimeout(() => {
success('P2 Success.');
}, 1000)
})

const p3 = new Promise((success, reject) => {
setTimeout(() => {
success('P3 Success.');
}, 2000)
})


const allPromise = Promise.race([p1, p2, p3]);

allPromise
.then(res => console.log(res))
.catch(err => console.error(err))

// Will get result after 1 second
// P2 Success.
  • The first finisher gets rejected
const p1 = new Promise((success, reject) => {
setTimeout(() => {
success('P1 Success.');
}, 3000)
})

const p2 = new Promise((success, reject) => {
setTimeout(() => {
reject('P2 Rejected.');
}, 1000)
})

const p3 = new Promise((success, reject) => {
setTimeout(() => {
success('P3 Success.');
}, 2000)
})


const allPromise = Promise.race([p1, p2, p3]);

allPromise
.then(res => console.log(res))
.catch(err => console.error(err))

// Will get result after 1 second
// P2 Rejected.

Promise.any

Similar to Promise.race but it will wait to get any first success result.

What if everything fails?

It rejects when all of the input's promises are rejected (including when an empty iterable is passed), with an AggregateError containing an array of rejection reasons.

  • The first finisher succeed
const p1 = new Promise((success, reject) => {
setTimeout(() => {
success('P1 Success.');
}, 3000)
})

const p2 = new Promise((success, reject) => {
setTimeout(() => {
success('P2 Success.');
}, 1000)
})

const p3 = new Promise((success, reject) => {
setTimeout(() => {
success('P3 Success.');
}, 2000)
})

const allPromise = Promise.any([p1, p2, p3]);
allPromise
.then(res => console.log(res))
.catch(err => console.error(err))
// Will get result after 1 second from p2
// P2 Success.
  • The first finisher gets rejected
const p1 = new Promise((success, reject) => {
setTimeout(() => {
success('P1 Success.');
}, 3000)
})

const p2 = new Promise((success, reject) => {
setTimeout(() => {
reject('P2 Rejected.');
}, 1000)
})
const p3 = new Promise((success, reject) => {
setTimeout(() => {
success('P3 Success.');
}, 2000)
})

const allPromise = Promise.any([p1, p2, p3]);
allPromise
.then(res => console.log(res))
.catch(err => console.error(err))

// Will get result after 2second from p3
// P3 Succeed.
  • All Promise gets rejected
const p1 = new Promise((success, reject) => {
setTimeout(() => {
reject('P1 Rejected.');
}, 3000)
})

const p2 = new Promise((success, reject) => {
setTimeout(() => {
reject('P2 Rejected.');
}, 1000)
})

const p3 = new Promise((success, reject) => {
setTimeout(() => {
reject('P3 Rejected.');
}, 2000)
})

const allPromise = Promise.any([p1, p2, p3]);
allPromise
.then(res => console.log(res))
.catch(err => console.error(err))

// Will get output after 3s
/*
ERROR!
[AggregateError: All promises were rejected] {
[errors]: [ 'P1 Rejected.', 'P2 Rejected.', 'P3 Rejected.' ]
}
*/

// We can get all errors through the err object like this `err.errors`
allPromise
.then(res => console.log(res))
.catch(err => console.log(err.errors));

// Output will be: [ 'P1 Rejected.', 'P2 Rejected.', 'P3 Rejected.' ]

Checkout my articles on Javascript:

--

--