Promise Definition
Similar to a promise that one can make to a fellow human, a Promise in JavaScript is an object with a callback function that will return an unknown value that may or may not be known at a future time. Consider the code example below:
let myWish = new Promise(function(resolve, reject){
})
console.log(myWish)
The output will be an object that contains a PromiseState property with a value of 'pending.'
A promise has three states which are pending, fulfilled, or rejected. The pending state represents a promise that is neither fulfilled nor rejected, a fulfilled promise represents successful completion and a rejected state shows that the operation failed and returns an error.
The Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value. Promises allow you to associate handler functions with an asynchronous action's eventual success value or failure reason. The eventual state of a pending promise can either be fulfilled with a value or rejected with an error. When either of these options occurs, the associated handlers queued up by a promise's then method are called.
Creating a Promise
A promise is created using the Promise JavaScript keyword. We then further provide a callback function that accepts two arguments; resolve and reject. Developers are allowed to use ES6 arrow functions if they want to.
The code that is written inside this function is the logic that the Promise object will use to settle the result of the caller function.
let myCar = 'Bugatti Chiron' // Global variable
// Creating the promise:
let myWish = new Promise(function(resolve, reject){
if(myCar == 'Bugatti Chiron'){
resolve("New car in the garage!")
} else {
reject("No new car for you.")
}
})
// Caller function that depends on the Promise previously created:
myWish.then(message => {console.log(message)})
The following methods are used in the caller function to provide different results depending on the logic of the Promise.
Promise.prototype.catch(): rejects the Promise and returns with an error. For example.catch(error => console.error(error))which outputs 'No new car for you' to the console.Promise.prototype.finally(): Appends a handler to the promise, and returns a new promise that is resolved when the original promise is resolved. The handler is called when the promise is settled, whether fulfilled or rejected. For examplePromise.finally(console.log("All processes complete")). More on Promise handler functions below.
Chaining Promises
We can append the previous caller function using chaining to allow the caller function to respond with an appropriate response as shown in code block below:
// Caller function:
myWish.then(message => {console.log(message)})
.catch(error => console.error(error))
.finally(console.log("All processes complete"))
The string 'All processes complete' will be displayed whether or not the Promise is resolved or rejected.
The .then() method takes up to two arguments; the first argument is a callback function for the fulfilled case of the promise, and the second argument is a callback function for the rejected case. Each .then() returns a newly generated promise object, which can optionally be used for chaining; for example:
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("At last!");
}, 3000);
});
myPromise
.then(handleFulfilledA, handleRejectedA)
.then(handleFulfilledB, handleRejectedB)
.then(handleFulfilledC, handleRejectedC);
Processing continues to the next link of the chain even when a .then() lacks a callback function that returns a Promise object. Therefore, a chain can safely omit every rejection callback function until the final .catch().
The code block above will not return anything because the callback functions inside the .then() methods have not been defined. Consider the more practical example below:
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("At last!");
}, 3000);
});
myPromise
.then((message) => console.log(message))
.then(() => console.log("Post credit..."))
.then(() => console.log("Post post credit..."))
The code above will wait for 3 seconds and then eventually output the resolve message and any other message chained afterward. We can also add the finally method to this using Promise.finally(operation...)
Promise methods
The table below shows all the methods that can be called on the Promise object.
Please note that these methods can be used together with chaining depending on the requirements of the function.
| Method | Functionality |
|---|---|
Promise.all() |
Creates a Promise that is resolved with an array of results when all of the provided Promises resolve, or rejected when any Promise is rejected. |
Promise.allSettled() |
Creates a Promise that is resolved with an array of results when all of the provided Promises resolve or reject. |
Promise.any() |
Accepts an array of Promises as arguments and returns the first one to resolve. |
Promise.catch() |
A chaining method used for error handling. It is executed when the Promise is rejected. Attaches a callback for only the rejection of the Promise. |
Promise.finally() |
The callback to execute when the Promise is settled (fulfilled or rejected). Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The resolved value cannot be modified from the callback. |
Promise.race() |
Accepts an array of Promises as arguments then creates a Promise that is resolved or rejected when any of the provided Promises are resolved or rejected. It returns a new Promise. |
Promise.reject() |
The reason the promise was rejected. This method creates a new rejected promise for the provided reason. |
Promise.resolve() |
Creates a new resolved promise. It returns the resolved message. |
Promise.then() |
The callback to execute when the Promise is resolved. It attaches callbacks for the resolution and/or rejection of the Promise. It returns a Promise for the completion of whichever callback is executed. |
Use Cases
Promises are useful for several purposes but the main reasons are the following:
- Creating asynchronous logic. Most frontend libraries like React and Vue.js make use of stateful components which may sometimes rely on data from the backend. Promises simplify the writing and execution of this logic.
- Returning data from a server. APIs sometimes require asynchronous functionality to respond to requests while other operations take place such as caching.
- Escaping "callback hell". This is a popular term that describes the complexity that comes with writing several callback functions inside a single function which results in difficulty reading code, and performance bottlenecks.
- Refactoring and readability. To support the point above, using Promises refactors your JavaScript code so that it is readable and easier to maintain.
JavaScript Promises are supported on all major browsers such as Chrome, Firefox, Opera, Safari, and Edge.
Wrapping Up
I'm glad I didn't promise anything at the beginning of this article but I hope you enjoyed reading it. If you'd like to share some feedback you can do so using my contact form. Thanks for reading until the end and I hope to see you again somewhere on the internet, until then, happy coding! ✌️
