Javascript Promise
JavaScript promises are a way to handle asynchronous operations. They provide a cleaner and more readable alternative to traditional callback functions. Promises represent a value that might be available now, or in the future, or never.
It is used to find out if the asynchronous operation is successfully completed or not.
A promise is an assurance or guarantee that something will happen in the future.With this assurance (promise) comes two possible outcomes–either fulfillment or failure.
Understanding Promises work
A promise is an object which can be returned synchronously from an asynchronous function.
JavaScript promises can be in one of three possible states. These states indicate the progress of the promise. They are:
- Pending : This is the default state of a defined promise, not yet fulfilled or rejected
- Fulfilled: This is the state of a successful promise,
onFulfilled()will be called (e.g.,resolve()was called) - Rejected: This is the state of a failed promise,
onRejected()will be called (e.g.,reject()was called)
A settled promise is a promise that has been either resolved (fulfilled) or rejected. Once a promise is settled, its state cannot be changed. In other words, the promise has completed its asynchronous operation, and the result (or reason for failure) is determined.
Once settled, a promise can not be resettled. Calling resolve() or reject() again will have no effect. The immutability of a settled promise is an important feature.
Creating Promises
//syntax
const myPromise = new Promise((resolve, reject) => {
resolve('resolved');
});
console.log('Start');
myPromise.then((result) => {
console.log('result', result);
});
console.log('End');
//Start
//End
//result resolvedThe ES6 promise constructor takes a anonymous function. That function takes two parameters, resolve(), and reject(). In the above example used only resolve() method, see .then() method executed after console start and end executes because .then() method works asynchronously.
In JavaScript, the .then method works asynchronously because it is part of the Promises/A+ specification, which dictates the behavior of promises. The asynchronous nature of .then is fundamental to how promises handle asynchronous operations and ensure non-blocking behavior in JavaScript.
When a promise is settled (either resolved or rejected), the associated .then or .catch callbacks are queued in the microtask queue. Microtasks are executed after the current execution context but before the next event loop cycle. This allows them to be processed quickly and consistently, ensuring that promise callbacks are executed in a predictable order.
const resolvePromise = Promise.resolve('resolved');
resolvePromise.then((result) => {
console.log('result', result);
});
// result resolved
const rejectPromise = Promise.reject('reject');
rejectPromise.catch((result) => {
console.log('result', result);
});
// result rejectasync function test() {
return 'javascript';
}
console.log('resultWithOutThen:', promise());
test().then((value) => {
console.log('resultWithThen:', value);
});
// resultWithOutThen: Promise { <unknown> }
// resultWithThen: javascriptIf keyword async used before function word, that function(test()) always returns the promise.
function test() {
return new Promise((resolve) => {
resolve('javascript');
});
}
test().then((value) => {
console.log('result:', value);
});The above code is another example of creating promise, function test() returns the promise
function calculateAge(age) {
return new Promise((resolve, reject) => {
if (age >= 18) resolve('Eligible');
else reject('Not Eligible');
});
}
calculateAge(25)
.then((value) => {
console.log('result:', value);
})
.catch((reason) => {
console.log('reason:', reason);
});
calculateAge(17)
.then((value) => {
console.log('result:', value);
})
.catch((reason) => {
console.log('reason:', reason);
});
// result: Eligible
// reason: Not EligibleIn the above code, the function calculateAge accepts one parameter and returns the promise and inside in the promise checking the age, if age greater than 18 will call the resolve method else call reject method
Another example without using catch block
function calculateAge(age) {
return new Promise((resolve, reject) => {
if (age >= 18) resolve('Eligible');
else reject('Not Eligible');
});
}
calculateAge(25).then(success, failure);
calculateAge(17).then(success, failure);
function success(value) {
console.log('result:', value);
}
function failure(reason) {
console.log('reason:', reason);
}
// result: Eligible
// reason: Not EligibleIn the above example we didn’t use the catch block to catch the errors, instead of the .then method accepts two call functions first one for success(resolve) and second method for handling the failure.
Promise chaining
Promise chaining is a powerful feature in JavaScript that allows you to perform a sequence of asynchronous operations in a more readable and maintainable way. Promises represent a value which might be available now, or in the future, or never. They are used to handle asynchronous operations, such as fetching data from a server, reading a file, or any other operation that takes time to complete.
Get Manikandan L’s stories in your inbox
Join Medium for free to get updates from this writer.
Here’s an example of promise chaining in JavaScript:
function operation1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('Operation 1 completed');
resolve('Result from Operation 1');
}, 1000);
});
}
function operation2(data) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('Operation 2 completed');
resolve(`Result from Operation 2 with ${data}`);
}, 1000);
});
}
operation1()
.then((result1) => {
console.log(result1);
return operation2(result1);
})
.then((result2) => {
console.log(result2);
})
.catch((error) => {
console.error('Error:', error);
});
// Operation 1 completed
// Result from Operation 1
// Operation 2 completed
// Result from Operation 2 with Result from Operation 1Note: .then() always returns a new promise only.
Promise Methods
Promise.All Method
The Promise.all() method accepts an iterable Object, such as an Array of promises as an input and returns a single promise that resolves to a result array of the input promises.When all input promises have been resolved or the iterable input does not contain a promise, the returned promise will be resolved.
It rejects immediately when an input promise rejects or non-promise throws an error and will reject with that first rejection message / error message.It is often used when there are many asynchronous tasks involved that the overall code relies on to work successfully — all of the ones we want to do before we continue to execute the code.This method aggregates the results of multiple promises.
const promiseOne = new Promise((resolve, reject) => {
setTimeout(resolve, 200, 'resolvedOne');
}); //will be resolved after 200ms
const promiseTwo = "Non-Promise"; //non-promise
const promiseThree = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'resolvedTwo');
}); // will be resolved after 100ms
Promise.all([promiseOne, promiseTwo, promiseThree])
.then((values) => {
console.log(values);
})
.catch((err) => {
console.log(err);
});
//expected output: [ 'resolvedOne', 'Non-Promise', 'resolvedTwo' ]Fulfilment of Promise.all()
When an empty iterable is passed, the promise returned by this method is executed synchronously. The resolved value will be an empty array. When a non-empty iterable is passed, and all of the promises fulfill or are not promises,the promise returned by this method will be executed asynchronously.
Rejection of Promise.all()
Promise.all() shows fail-fast behavior, that is, Promise.all() asynchronously rejects with the value of the promise that rejected, if any of the passed-in elements are rejected.
For example, if we pass in one promises that resolve after a timeout and one promise that rejects immediately, then Promise.all() will reject immediately. It does not depend on if the other promises have resolved. The following example just does the same!
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('one'), 1000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => reject('failure'), 2000);
});
const promise3 = new Promise((resolve, reject) => {
setTimeout(() => resolve('three'), 3000);
});
Promise.all([promise1, promise2, promise3])
.then((values) => {
console.log(values);
})
.catch((error) => {
console.log(error);
});
//expected output: failurePromise.any() Method
The Promise.any() takes an iterable Object, such as an Array of promises as an input. Once a promise is fulfilled, a single promise is returned and the promise is resolved using the value of the promise. If no promises in the iterable fulfil (if all of the given promises are rejected), then the returned promise is rejected with an AggregateError (that groups together individual errors).
Resolve of Promise.any()
It’s a method for working with promises, allowing you to work with multiple promises and fulfill the returned promise as soon as one of the input promises is fulfilled. If all the input promises are rejected, then the returned promise is rejected.
Unlike Promise.all(), this method is used to return the first promise that fulfils. It is short-circuited right after a promise is completed, so as soon as a promise is fulfilled, it will not wait for other promises to complete.When a non-empty iterable is passed, and any of the promises fulfill, or are non-promises, then the promise returned by this method is fulfilled asynchronously.
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Promise 1 resolved');
}, 1000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('Promise 2 rejected');
}, 500);
});
const promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Promise 3 resolved');
}, 1500);
});
Promise.any([promise1, promise2, promise3])
.then((value) => {
console.log(value); // Output: 'Promise 1 resolved'
})
.catch((reason) => {
console.log(reason); // This will not be executed in this example
});Rejection of Promise.any()
In this example, Promise.any is used to wait for the first promise to be fulfilled (or rejected). In this case, since promise1 is the first to resolve, the .then block will be executed, and the value 'Promise 1 resolved' will be logged.
It’s important to note that if all promises are rejected, the .catch block will be executed with an array of rejection reasons.
const promises = [
Promise.reject('Promise 1 rejected'),
Promise.reject('Promise 2 rejected'),
Promise.reject('Promise 3 rejected')
];
Promise.any(promises)
.then((value) => {
console.log(value); // This will not be executed in this example
})
.catch((reasons) => {
console.log(reasons); // Output: ['Promise 1 rejected', 'Promise 2 rejected', 'Promise 3 rejected']
});Promise.race() Method
The Promise.race() method returns a Promise that is resolved or rejected, as soon as one of the promises in an iterable, such as an array, fulfils or rejects, with the value or reason from that Promise.The promise returned will be forever pending, if the iterable passed is empty.Promise.race() will resolve to the first value found in the iterable, if the iterable contains one or more non-promise value or an already settled promise.
const pro1 = new Promise((resolve, reject) => {
setTimeout(() => resolve("one"), 200);
});
const pro2 = new Promise((resolve, reject) => {
setTimeout(() => resolve("two"), 100);
});
Promise.race([pro1, pro2])
.then((response) => {
console.log(response); //output: two
})
.catch((err) => {
console.log(err);
});
const pro3 = new Promise((resolve, reject) => {
setTimeout(() => reject("rejected"), 300);
});
const pro4 = new Promise((resolve, reject) => {
setTimeout(() => resolve("four"), 400);
});
Promise.race([pro3, pro4])
.then((response) => {
console.log(response);
})
.catch((err) => {
console.log(err);
}); //output: rejectedIn the first case, pro2(100ms) is faster than pro1(200ms), so the output shows two. In the second case, pro3(300ms) is faster than pro4(400ms), so the promise rejects. So basically, Promise.race() takes the first settled Promise
If we take this same example and pass all the promises inside Promise.race(), it will return two as output, as the pro2 is the fastest promise to be settled in this case.
const pro1 = new Promise((resolve, reject) => {
setTimeout(() => resolve("one"), 200);
});
const pro2 = new Promise((resolve, reject) => {
setTimeout(() => resolve("two"), 100);
});
const pro3 = new Promise((resolve, reject) => {
setTimeout(() => reject("rejected"), 300);
});
const pro4 = new Promise((resolve, reject) => {
setTimeout(() => resolve("four"), 400);
});
Promise.race([pro1, pro2, pro3, pro4])
.then((response) => {
console.log(response);
})
.catch((err) => {
console.log(err);
});
//output: twoPromise.finally()
Promise.finally is another feature introduced in ECMAScript 2018 (ES9) for working with JavaScript promises. It allows you to register a callback that is called when the promise is settled, regardless of whether the promise is fulfilled or rejected. The finally method is useful for performing cleanup or finalization tasks that should be executed regardless of the outcome of the promise.
const someAsyncOperation = new Promise((resolve, reject) => {
// Simulating an asynchronous operation
setTimeout(() => {
// Resolve the promise
resolve('Operation completed successfully');
// or reject the promise
// reject('Operation failed');
}, 1000);
});
someAsyncOperation
.then((result) => {
console.log(result); // Output: 'Operation completed successfully'
})
.catch((error) => {
console.error(error); // This block will not be executed if the promise is resolved
})
.finally(() => {
console.log('This will be executed regardless of success or failure');
});In this example, the finally block will be executed no matter if the promise is fulfilled (resolved) or rejected. This can be useful for tasks such as cleaning up resources, closing connections, or releasing acquired locks.
It’s important to note that the finally method returns a new promise that will be resolved with the original promise's result or rejected with its rejection reason after the finally callback is executed. This allows you to continue chaining .then and .catch after the finally block.
someAsyncOperation
.then((result) => {
console.log(result);
})
.catch((error) => {
console.error(error);
})
.finally(() => {
console.log('This will be executed regardless of success or failure');
})
.then(() => {
console.log('This will be executed after the finally block');
});Promise.finally simplifies the process of adding cleanup logic to promise chains and makes the code more readable and maintainable.







