Promises in JavaScript

Challenge Inside! : Find out where you stand! Try quiz, solve problems & win rewards!

Learn via video course

Javascript Course - Mastering the Fundamentals
Javascript Course - Mastering the Fundamentals
By Mrinal Bhattacharya
Free
star4.8
Enrolled: 1000
Javascript Course - Mastering the Fundamentals
Javascript Course - Mastering the Fundamentals
Mrinal Bhattacharya
Free
4.8
icon_usercirclecheck-01Enrolled: 1000
Start Learning

Overview

JavaScript, a single-threaded language, executes code statements sequentially. Some statements execute instantly, while others take longer, such as making server requests. This can result in increased loading times and a poor user experience. To address this issue, promises were introduced. Promises in javascript originated in C++ during the Xanadu project and were later adopted by languages like E, influenced by Python developers. Early versions of promises were implemented in Concurrent Prolog and MultiLisp. The term promise is javascript was coined by Liuba Shrira and Barbara Liskov. Prior to promises, callbacks were used to handle asynchronous code, leading to a problem known as Callback Hell. However, when promise in javascript emerged, their adoption was not immediate. In 2007, the Dojo framework introduced the Dojo.deferred object, incorporating promises into JavaScript. Subsequently, in 2009, Node.js integrated promises into its non-blocking API.

What are Promises in JavaScript, Why do we need it?

A promise in JavaScript is no different from a promise in real life. If you take a promise from me that I will bring you a bar of chocolate, there can be three cases here:

  1. You are waiting for me to come (the promise is in a PENDING state)
  2. I bought you a bar of chocolate (the promise is in a FULFILLED state)
  3. The shop was closed, or due to any reason, the promise failed ( the promise goes to the REJECTED state).

Similarly, when you call an asynchronous function, i.e., a function that might send a response in the future, you are actually taking a promise. At first, the promise remains in the PENDING state because no response has been received. After some time, when the response is received, there are two possible cases:

  1. We got a response (i.e., the promise goes to the FULFILLED state)
  2. We got an error and no response (the promise got REJECTED)

Note: Getting a response means the promise is in the SETTLED state. In other words, the promise is not in the PENDING state now, and it has been either RESOLVED or REJECTED.

What is a Promise in JavaScript

  • To handle asynchronous tasks in JavaScript
  • To handle blocking of execution thread because asynchronous tasks take time to complete.

JavaScript is single-threaded, i.e., it can perform only one task at a time. In other words, it will execute the following line of instruction only after the first one is complete. But in that case, what if one task takes a significantly long time? In that case, it will block the thread for a long time, which will negatively affect the performance of our application. These are called asynchronous tasks.

This is why we need ways to handle asynchronous tasks carefully. We need a way in which these time-consuming tasks do not block the thread, and other tasks can be carried out meanwhile the asynchronous tasks get their response. We need a way to get notified when the asynchronous task gets resolved so that we can do something related to that task after it has been completed.

We have ways to handle the same. Promises in JavaScript are one of them. Let's understand promises in JavaScript.

Callback Vs Promises for Asynchronous Operations

Callback and promise in javascript are both used to handle asynchronous code in JavaScript. When you use callbacks, you send callback functions as parameters to another function. Using promises, you do not need to PASS callback functions. But instead, ATTACH them with the promise using the .then() method.

So, you still need to use callbacks while working with promise in javascript, but differently.

Let us look at both in a little more detail:

JavaScript Callbacks

  • Code written with promises is easy to read and maintain.
  • Error handling is quite easy with JavaScript promises.
  • Example:

Callbacks

  • Syntax is not easy to read and understand.
  • Error handling is not so easy with callbacks.
  • Example:

How Promises Work

In technical terms, a promise in javascript is an object that is returned as a response to an asynchronous function. It can be one of these four possible states:

  1. PENDING
  2. FULFILLED
  3. REJECTED
  4. SETTLED

promise pending and fulfilled

promise pending and rejected

Let us understand this in a bit more detail. The moment a promise is invoked, it goes to the PENDINGstate and waits for aresponseto come. We might get the correct response, which makes the promise FULFILLED, or we might get an error, i.e., the promise goes to the REJECTED state. Apromiseis said to beSETTLEDif it is not in thepending state`, i.e., either the response has arrived or an error has occurred.

Note: Once settled, a promise cannot be resettled.

The Important Rules of Promise

A+ Specification defined the standards for promises. JavaScript ECMAScript 2015 promises to follow these rules. Listed below are the rules set for the promises:

  1. A standard promise comes with a pre-defined .then() method.
  2. A promise is initially in the pending state, which transitions into a fulfilled or rejected state after some time.
  3. Fulfilled or rejected states are settled states. A promise, once settled, cannot be resettled, i.e., a settled state can not transition into any other state.
  4. Once settled, a promise will have value. If fulfilled, the promise will have the data requested. If rejected, it will contain undefined. But it will always have a value after being settled, which cannot be changed.

The Important Rules of Promise

Promise Chaining

.then() always returns another promise. So, we can chain promises like this.

Promise Chaining works like try, and catch, but it makes asynchronous code look more like the synchronous one. The code here works sequentially, i.e., line by line.

Thenable Promises

Thenable promises in JavaScript are objects that have a "then()" method, allowing them to be treated similarly to standard promises. They are commonly used as a bridge between third-party libraries or legacy code and native promises. Thenable promises can be used in place of regular promises by implementing the "then()" method and returning a new promise. This enables interoperability between different promise implementations and ensures consistent behavior. It allows developers to work with promises from various sources without having to modify existing code. Example

Incumbent settings object tracking

Constructor

  • Create your own promise using the Promise() constructor.
  • Pass the executor function as the parameter to the constructor.
  • The executor function gets executed whenever the promise is created.

We have a promise() constructor in JavaScript. This is used to wrap those functions which do not support promises by default.

Syntax

Executor Function is passed as a parameter to the Promise constructor. It is executed by the constructor function whenever a new promise is created using the constructor.

We can create promises using the Promise constructor in the following way:

Static methods

  • Promise.all() resolves when all the promises resolve.
  • Promise.allSettled() resolves when all the promises settle, i.e., either fulfill or get rejected.
  • Promise.any() resolves as soon as any one of the promises resolves.
  • Promise.race() resolves as soon as any one of the promises settles, i.e., either fulfills or gets rejected.
  • promise.reject(reason) return a new rejected promise with the reason for rejection some as passed.
  • promise.resolve(value) return a new resolved promise with the value some as passed.
  • Promise.all(iterable) This method takes an iterable of promises and resolves to a single promise that resolves when either all the promises in the iterable resolves or any one rejects.

  • Case I: Resolves when all the promises resolve: In this case, the final resolved value will be an array containing aggregated values from all the promises from the iterable.

  • Case II: Resolves when any one of the rejects: In this case, the reason for the rejected promise is the reason the first promise got rejected from the iterable.

  • Promise.allSettled(iterable) This method takes an iterable of promises and returns a new promise only when all the promises get settled, i.e., are in fulfilled or rejected state. This method is mainly used when the promises passed in the iterable are not connected to each other, and we just want to know what they result in.

On the other hand, Promise.all() is used when the promises are interdependent, and we want to stop the execution when any one of them gets rejected.

  • Promise.any(iterable) This method takes an iterable of promises, and as soon as one of the promises gets resolved, it gets resolved with the value of the first resolved promise.

If none of the promises gets resolved, i.e., all of them get rejected, then the promise gets rejected with an aggregated error formed by grouping all the individual errors.

  • Promise.race(iterable) This method takes an iterable of promises and returns a promise that fulfills or rejects as soon as any one of the promises from the iterable gets fulfilled or rejected.

If fulfilled, the final returned promise has the value of the fulfilled promise. If rejected, it picks the reason from the rejected promise.

  • Promise.reject(reason) This method is used to return a new rejected promise with the reason same as the one passed as the parameter.
  • Promise.resolve(value) This method returns a resolved promise with the value passed as the parameter. If the value is thenable (i.e., then can be used ), it will follow the then's and then return the final value.

Note: In cases where we do not know if a value is a promise, promise.resolve() it, and it will return the value as a promise.

Instance Methods

  • Promise.prototype.then() handles the value for the promise in case it is fulfilled successfully.
  • Promise.prototype.catch() handles the promise in case an error occurs or the promise gets rejected.
  • Promise.prototype.finally() runs a piece of code as soon as the promise settles, regardless of the promise's status - fulfilled or rejected.
  • Promise.prototype.then() This method takes in two parameters - callback functions for success and failure (both optional) and returns a promise.

Syntax: p.then(onFulfilled[, onRejected]); Parameters

  1. onFulfilled This function is called if the promise fulfills.
  2. onRejected This function is called if the promise rejects.

Note: Both these functions are optional.

Example:

  • Promise.Prototype.catch() This method handles errors for the promise. If an error occurs, the catch method executes the error handler function passed as a parameter to the catch method.

Syntax: p.catch(onRejected); Parameter: onRejected is the function to be called when an error occurs or the promise gets rejected.

Example:

Note: In the previous section, we handled the error using the second argument to the then() method, and in this section, we're handling the same using the catch() method. Which one is the right way to do it?

Both of them are internally the same. The catch() method internally uses then(onFulfilled, onRejected) to handle the rejected promise! So, a promise can be set to handle errors/rejections both ways.

  • Promise.Prototype.finally() This method returns a new promise when the promise finally goes to the settled state, i.e., it gets fulfilled or gets rejected. This is a handy method when we want to run a piece of code when the promise settles, no matter if it gets rejected or fulfilled.

Syntax: p.finally(onFinally); Parameter: onFinally is the function that is executed when the promise either fullfills or gets rejected.

Example:

How to Cancel a Promise?

We cannot cancel a promise using modern JavaScript. But you can certainly reject a promise with a reason 'Cancelled.'

Note: Do not use .cancel() because it violates the standard rules for creating promises because the function creating the promise should only be allowed to reject or fulfill or cancel that.

Rejected Promises in JavaScript

  • A promise is an object that contains the results of an asynchronous function.

So, how do we extract the result from that object once it gets fulfilled successfully? Or how do we get to know that the promise got rejected or if it faced an error? And at last, how do we handle errors/rejections here?

Extracting the Promise Fulfilled value

The then() method (as discussed earlier in the Instance Methods Section) is used to extract the result from the async task.

Syntax:

  • Extracting the Promise Rejection Error

.catch() method is used to extract out the reason for rejection (if the promise rejects).

Syntax:

Extracting Value and Error

Since both .then() and .catch() return a new promise, they can be chained. And both the cases - promise getting fulfilled or getting rejected can be handled using this syntax:

The Native JS Promise Extras

The native Promise object offers additional functionalities that may capture your interest. These features include:

Promise.reject(): This function returns a promise that is rejected, allowing you to handle errors or exceptional cases in your code. Promise.resolve(): Conversely, Promise.resolve() returns a promise that is resolved, enabling you to handle successful outcomes or values in your code. Promise.race(): By providing an array or any iterable, Promise.race() returns a promise that resolves with the value of the first promise that is resolved within the iterable. If any promise within the iterable rejects, the returned promise will be rejected with the corresponding reason. Promise.all(): Taking an array or any iterable as input, Promise.all() returns a promise that resolves only when all the promises within the iterable have resolved successfully. However, if any of the promises reject, the returned promise will be rejected with the reason of the first rejected promise encountered.

Benefits of Promises

  • Promises are better than Callbacks as callbacks create a situation called callback hell
  • Code is readable and better maintained when promises are used to handle async code than when callbacks are used.

There can be two types of tasks that can be carried out in javascript:

  1. Synchronous tasks
  2. Asynchronous tasks

JavaScript works very well with synchronous or non-blocking code. Consider this example:

We want to getData and then console it. Since getData is an asynchronous task, i.e., it will return the data immediately, this piece of code works perfectly fine.

But what if getData needs to make a request to the database to get data and then return the data? This task will take time, and hence, this becomes an asynchronous task. Our output, in this case, will be undefined because javascript will look for an immediate answer but will not get one. Hence, it will return undefined as data.

When it comes to asynchronous tasks, i.e., where we might not get a response immediately, we need to handle this in a very different way. One of the ways is to use callbacks. Consider this:

We are sending the action to be performed after the completion of the asynchronous task as a parameter to the first function. Suppose we want to filter the data and display an image corresponding to this id.

Note: Loading an image from the database will also need time and is an asynchronous task.

Now, this can lead to  callback hell,

callback hell

So, we have promises to resolve this issue. then() and catch() in JavaScript help us process the fulfilled and rejected response much more efficiently and easily.

Look at this, the same code with callback being written with promises.

The flow of code here is much easier to understand and readable. Hence, some of the evident benefits of using promises are:

  1. Helps us handle asynchronous tasks in a much more efficient way
  2. Helps improve the code quality and readability.
  3. Better error handling using catch().
  4. Avoids callback hell and improves the flow of control definition

Examples

Example 1:

Look at a few examples to understand promises in detail:

Output:

output of the example1

Explanation: A promise is created using the Promise() constructor in the code above. This promise will resolve or reject based on the value of the count variable.

  • On consoling out promiseCount, we get a promise as the output. But how do we extract the response received? And what about the case our promise rejects because of the negative value of the count variable?
  • We can use .then() to extract out the value of the fulfilled promise and .catch() to extract out the reason for rejection (in case it gets rejected) in the following way:

Output:

output of example1

Example 2:

Now let us look at a more realistic example. In every web application, we need to send requests to the server for data to be displayed to the view (on the browser).

This request takes some to come back with the requested data. At times, the request might encounter an error while processing. But our web app cannot wait for such long. Hence, we use promises to handle this task.

We'll use the fetch() method to make a request to the server and get a random fact about cats. fetch() returns a promise, so we can chain this to get the response out:

Output:

output of example2

Browser Compatibility

Shown below is the table of browser compatibility for promises in JavaScript for almost all the browsers we use today.

Browser Compatibilities

FAQs

Q1. What are Promises in JavaScript and why do we need them? Ans: Promises in JavaScript are objects that represent the eventual completion (or failure) of an completely asynchronous operation. They are used to handle asynchronous code and avoid blocking the execution thread. Promises allow us to work with asynchronous tasks in a more structured and manageable way. Q2. What is the difference between Callbacks and Promises for handling asynchronous operations in JavaScript? Ans: Callbacks and Promises are both used to handle asynchronous code in JavaScript. With callbacks, you pass callback functions as parameters to another function, while with Promises, you attach the callbacks to the Promise using the .then() method. Promises provide a more readable and maintainable code structure compared to callbacks, and they also make error handling easier. Q3. How do Promises work in JavaScript? Ans: A Promise in JavaScript can be in one of four possible states: PENDING, FULFILLED, REJECTED, or SETTLED. When a Promise is invoked, it enters the PENDING state and waits for a response. It can then transition to the FULFILLED state if the response is received successfully, or to the REJECTED state if an error occurs. Once settled, a Promise cannot be resettled. Promises follow certain rules defined in the A+ Specification. Q4. What are some static methods and instance methods of Promises in JavaScript? Ans: Static methods include Promise.all(), Promise.allSettled(), Promise.any(), Promise.race(), Promise.reject(), and Promise.resolve(). These methods provide various ways to handle multiple Promises and create new Promises. Instance methods include Promise.prototype.then(), Promise.prototype.catch(), and Promise.prototype.finally(). These methods are used to handle the value and errors of a Promise and perform actions when the Promise settles.

Conclusion

  • JavaScript can perform only one task at a time, and if a task takes longer to complete, the JavaScript thread cannot wait for it. Asynchronous tasks need to be handled differently.
  • To handle asynchronous tasks effectively, we have promises in JavaScript.
  • Promises are exactly what you would expect from its name. They make a promise that a response will be sent to the browser. Meanwhile, other tasks can be performed.
  • Promises can have four states - PENDING, FULFILLED, REJECTED, and SETTLED.
  • When a promise is triggered, it is in the Pending state. After some time, this asynchronous task might be completed and return with one of these statuses - fulfilled or rejected.
  • A promise fulfilled or rejected is said to be in the SETTLED state. Once settled, a promise cannot be resettled.
  • How does the browser get to know when a promise gets a response? We have the following methods for the same.