JS Promise Orchestration

A Promise can either resolve into a result or reject into an error. In this blog we will see how we can wait for multiple promises, depending on our requirements and use cases. We will cover Promise.all, Promise.allSettled, Promise.race and Promise.any functions in particular. We will see example snippets and analyze the output for better understanding.

Let’s define the common functions which will be used in our example snippets. As these functions are common, we will define them only once, to avoid repetition. These will be used in all the later snippets. resolveAfter returns a promise which resolves after the timeout (in seconds), which is passed as the argument. rejectAfter returns a promise which rejects after the timeout (in seconds), which is passed as the argument.

// Returns a promise which resolves after timeout seconds
const resolveAfter = function(name, timeout) {
	return new Promise((onResolve, onReject) => {
		setTimeout(() => {
			console.log(`Promise ${name} resolved after: ${timeout} second(s)`);
			onResolve(`Resolved: ${name}`)
		}, timeout * 1000);
	});
}

// Returns a promise which rejects after timeout seconds
const rejectAfter = function(name, timeout) {
	return new Promise((onResolve, onReject) => {
		setTimeout(() => {
			console.log(`Promise ${name} rejected after: ${timeout} second(s)`);
			onReject(`Rejected: ${name}`)
		}, timeout * 1000);
	});
}

Promise.all

Promise.all function takes in an array of promises as the argument. It returns a promise, which gets resolved or rejected as per the following:

  • It resolves, if all the promises in the Array are resolved, i.e. it waits for all the promises to get resolved.
  • It rejects, as soon as any one promise in the Array is rejected.

The return value is an Array of the results from the promises in the same order as the promise Array.

Now let’s see some examples along with their output.

Example 1 - All three promises resolved

Promise.all([resolveAfter('one', 1), resolveAfter('two', 2), resolveAfter('three', 3)])
	.then((results) => {
		console.log('Promise.all resolved.');
		console.log('results:', results);
	})
	.catch((error) => {
		console.log('Promise.all rejected.');
	});

Following is the output. Promise.all waits for all the promises to get resolved.

> node test.js 
Promise one resolved after: 1 second(s)
Promise two resolved after: 2 second(s)
Promise three resolved after: 3 second(s)
Promise.all resolved.
results: [ 'Resolved: one', 'Resolved: two', 'Resolved: three' ]

Example 2 - Two promises resolved, one rejected

Promise.all([resolveAfter('one', 1), rejectAfter('two', 2), resolveAfter('three', 3)])
	.then((results) => {
		console.log('Promise.all resolved.');
		console.log('results:', results);
	})
	.catch((error) => {
		console.log('Promise.all rejected.');
	});

Following is the output. Promise.all rejects as soon as the first reject is obtained. It does not wait for the third promise to get settled (resolved or rejected).

> node test.js 
Promise one resolved after: 1 second(s)
Promise two rejected after: 2 second(s)
Promise.all rejected.
Promise three resolved after: 3 second(s)

Promise.allSettled

Promise.allSettled function takes in an array of promises as the argument. It returns a promise, which gets resolved after each of the input promises is settled (resolved or rejected).

The return value is an array of objects with keys - status and value. status can be fulfilled or rejected. value is result of resolution or error of rejection. The order of the objects in the result array corresponds to the order of the input promise array.

Now let’s see some examples along with their output.

Example 1 - All three promises resolved

Promise.allSettled([resolveAfter('one', 1), resolveAfter('two', 2), resolveAfter('three', 3)])
	.then((results) => {
		console.log('Promise.allSettled resolved.');
		console.log('results:', results);
	})
	.catch((error) => {
		console.log('Promise.allSettled rejected.');
	});

Following is the output. Promise.allSettled resolves after all the input promises are settled (resolved in this case).

> node test.js 
Promise one resolved after: 1 second(s)
Promise two resolved after: 2 second(s)
Promise three resolved after: 3 second(s)
Promise.allSettled resolved.
results: [
  { status: 'fulfilled', value: 'Resolved: one' },
  { status: 'fulfilled', value: 'Resolved: two' },
  { status: 'fulfilled', value: 'Resolved: three' }
]

Example 2 - Two promises resolved, one rejected

Promise.allSettled([resolveAfter('one', 1), rejectAfter('two', 2), resolveAfter('three', 3)])
	.then((results) => {
		console.log('Promise.allSettled resolved.');
		console.log('results:', results);
	})
	.catch((error) => {
		console.log('Promise.allSettled rejected.');
	});

Following is the output. Promise.allSettled resolves after all the input promises are settled (two resolved and one rejected in this case).

> node test.js 
Promise one resolved after: 1 second(s)
Promise two rejected after: 2 second(s)
Promise three resolved after: 3 second(s)
Promise.allSettled resolved.
results: [
  { status: 'fulfilled', value: 'Resolved: one' },
  { status: 'rejected', reason: 'Rejected: two' },
  { status: 'fulfilled', value: 'Resolved: three' }
]

Promise.race

Promise.race function takes in an array of promises as the argument. It returns a promise, which gets resolved or rejected as soon as the first promise in the input promise array is resolved or rejected.

The return value is the same as the resolved value of the first resolved promise. In the case of rejection, the reason for the rejection is the same as that given by the first rejected promise.

Now let’s see some examples along with their output.

Example 1 - All three promises resolved

Promise.race([resolveAfter('one', 1), resolveAfter('two', 2), resolveAfter('three', 3)])
	.then((results) => {
		console.log('Promise.race resolved.');
		console.log('results:', results);
	})
	.catch((error) => {
		console.log('Promise.race rejected.');
	});

Following is the output. Promise.race resolves after the first promise resolves.

> node test.js 
Promise one resolved after: 1 second(s)
Promise.race resolved.
results: Resolved: one
Promise two resolved after: 2 second(s)
Promise three resolved after: 3 second(s)

Example 2 - Two promises resolved, one rejected (Time-wise first one resolved)

Promise.race([resolveAfter('one', 1), rejectAfter('two', 2), resolveAfter('three', 3)])
	.then((results) => {
		console.log('Promise.race resolved.');
		console.log('results:', results);
	})
	.catch((error) => {
		console.log('Promise.race rejected.');
	});

Following is the output. Promise.race resolves after the first promise resolves.

> node test.js 
Promise one resolved after: 1 second(s)
Promise.race resolved.
results: Resolved: one
Promise two rejected after: 2 second(s)
Promise three resolved after: 3 second(s)

Example 3 - Two promises resolved, one rejected (Time-wise first one rejected)

Promise.race([rejectAfter('one', 1), resolveAfter('two', 2), resolveAfter('three', 3)])
	.then((results) => {
		console.log('Promise.race resolved.');
		console.log('results:', results);
	})
	.catch((error) => {
		console.log('Promise.race rejected.');
	});

Following is the output. Promise.race rejects after the first promise rejects.

> node test.js 
Promise one rejected after: 1 second(s)
Promise.race rejected.
Promise two resolved after: 2 second(s)
Promise three resolved after: 3 second(s)

Promise.any

Promise.any function takes in an array of promises as the argument. It returns a promise, which gets resolved at the first resolution amongst the promise array. If no promise resolves, then Promise.any rejects.

So it waits for any of the promises to get resolved. If it does not find any resolutions, then it rejects.

In the case of resolution, the resolved value is the same as the resolved value of the first resolved promise.

Now let’s see some examples along with their output.

Example 1 - Time-wise second promise resolved. Others rejected.

Promise.any([rejectAfter('one', 1), resolveAfter('two', 2), rejectAfter('three', 3)])
	.then((results) => {
		console.log('Promise.any resolved.');
		console.log('results:', results);
	})
	.catch((error) => {
		console.log('Promise.any rejected.');
	});

Following is the output. Promise.any resolves after the second promise resolves.

> node test.js 
Promise one rejected after: 1 second(s)
Promise two resolved after: 2 second(s)
Promise.any resolved.
results: Resolved: two
Promise three rejected after: 3 second(s)

Example 2 - All promises rejected.

Promise.any([rejectAfter('one', 1), rejectAfter('two', 2), rejectAfter('three', 3)])
	.then((results) => {
		console.log('Promise.any resolved.');
		console.log('results:', results);
	})
	.catch((error) => {
		console.log('Promise.any rejected.');
	});

Following is the output. Promise.any resolves after all the promises reject.

> node test.js 
Promise one rejected after: 1 second(s)
Promise two rejected after: 2 second(s)
Promise three rejected after: 3 second(s)
Promise.any rejected.
Kedar Chandrayan

Kedar Chandrayan

I focus on understanding the WHY of each requirement. Once this is clear, then HOW becomes easy. In my blogs too, I try to take the same approach.