DEV Community

Cover image for Everything about Promises in JavaScript
Junaid Shaikh
Junaid Shaikh

Posted on

Everything about Promises in JavaScript

Hey folks! 👋 Today, in this post we will look at one of the important concepts of JavaScript and that is Promises. We will look at what is a promise, consumer methods, and promise static methods. So, let's get started. This is going to be a bit long post.

Introduction to Promise

A promise in JavaScript is an object that represents the completion or failure of an asynchronous operation and its result. That means we always get something back from the promise, whether it will be a success value or the error value.

A promise can be in one of the following states

  • pending - the initial state, the results are yet to be decided
  • fulfilled - represents the successful completion of the task
  • rejected - represents the failure of the task

A promise is settled when it is either fulfilled or rejected.

Create your own promise

We can create a promise using the promise constructor

const promise = new Promise((resolve, reject) => {
  // your asynchronous task
});
Enter fullscreen mode Exit fullscreen mode

A new promise object is returned from the constructor. The promise constructor needs an executor function to be passed, and the resolve and reject callbacks are passed by the JavaScript itself.

The executor function is executed immediately when new Promise is called. The work of the executor function is to produce a value. Whenever it gets the value, it should call resolve or reject based on the results.

When resolve is called promise moves to the fulfilled state, and when reject is called promise moves to the rejected state.

Now, let's do some work inside the executor function

const promise = new Promise((resolve, reject) => {
  // this function is executed immediately when a new promise is created

  // the promise will be resolve with value 5 after 1 second.
  setTimeout(() => {
    resolve(5);
  }, 1000);
});
Enter fullscreen mode Exit fullscreen mode

The executor function contains an asynchronous task, the promise will be resolved with a value of 5 after 1 second. The promise object has two hidden properties [[PromiseState]] and [[PromiseResult]] which keep track of the current state of promise and the resulting value.

Promise Object

We can also directly resolve the promise without waiting for any action to take place.

const promise = new Promise((resolve, reject) => {
  // the promise will be resolved immediately 
  resolve(5)

  // any resolve or reject call after this will be ignored 
  reject('Error'); // ignored
})
Enter fullscreen mode Exit fullscreen mode

That's how we create a new promise.

Accessing the promise results

We can not directly access the state and result property of the promise. We can access the result value using .then, .catch, and .finally promise methods.

All the above methods return a new promise which means they can be further chained to do some other work based on the result of a previous promise. That is something called promise chaining.

then

.then method accepts up to two arguments. The first one is the function executed when the promise is fulfilled, the second argument is the function which executed when the promise is rejected.

promise.then((result) => {
  // here handle fulfilled promise
}, (error) => {
  // handle rejected promise
})
Enter fullscreen mode Exit fullscreen mode

The second argument is optional.

Let's look at the example

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(5);
  }, 1000);
});

promise.then(
  (result) => console.log(result),  // 5
  (error) => console.log(error)  // doesn't run
); 
Enter fullscreen mode Exit fullscreen mode

Here is the reaction to the rejected promise

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("Promise rejected");
  }, 1000);
});

promise.then(
  (result) => console.log(result), // doesn' run
  (error) => console.log(error) // Promise rejected
);
Enter fullscreen mode Exit fullscreen mode

catch

What if we are only interested in the errors? We can handle this in two ways, we can pass the first argument to .then as null and provide the second callback function or we can use the .catch method.

The .catch requires a function that will be executed when the promise is rejected and get an error value as the argument.

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("Promise rejected");
  }, 1000);
});

promise.catch(
  (error) => console.log(error) // Promise rejected
);
Enter fullscreen mode Exit fullscreen mode

finally

The .finally method requires a function that will be executed when the promise is settled. A promise is said to be settled when it is either fulfilled or rejected.

The function passed to the .finally doesn't get any arguments. That means it doesn't know whether the promise is fulfilled or rejected. We can perform clean-up inside this handler like stopping the loader.

promise.finally(() => {
  // perform clean up
});
Enter fullscreen mode Exit fullscreen mode

The resolved value or rejected value is passed through the finally handler. We can access the value by chaining .then or .catch to the .finally method.

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(5);
  }, 1000);
});

promise
  .finally(() => {
    // perform clean up
  })
  .then((result) => console.log(result)); // 5
Enter fullscreen mode Exit fullscreen mode

This way we handle the result of the promise using .then, .catch, and .finally.

Promise static methods

There are 6 static methods that we will look at.

Promise.all()

Promise.all() method allows one to process several promises in parallel and wait until all the promises are resolved. It returns a single promise that resolves the array of all promises' results.

If any one of the promises in the list of promises passed is rejected then Promise.all() is immediately rejected and other promises are ignored.

Syntax

Promise.all(iterable)
Enter fullscreen mode Exit fullscreen mode

Here iterable is generally an array of promises.

An example,

const promise1 = new Promise((resolve) =>
  setTimeout(() => resolve("promise1"), 3000)
);

const promise2 = new Promise((resolve) =>
  setTimeout(() => resolve("promise2"), 3000)
);

const listOfPromise = [promise1, promise2];

Promise.all(listOfPromise).then((results) => console.log(results)); // ["promise1", "promise2"]
Enter fullscreen mode Exit fullscreen mode

Note that the resulting array will get be in the same order as the input array.

Promise.allSettled()

Promise.all() is rejected as a whole if one of the promises provided is rejected. But sometimes we need the result of every promise regardless of their results. That's where we can use Promise.allSettled()

Promise.allSettled() takes an iterable of promises and returns a new Promise which is resolved to an array of objects having information about the status and value of each promise.

for the fulfilled promise, the object looks like

{ status: "fulfilled" , value: result }

for the rejected promise, the object looks like

{ status: "rejected", value: error }

const promise1 = new Promise((resolve) =>
  setTimeout(() => resolve("promise1"), 3000)
);

const promise2 = new Promise((resolve, reject) =>
  setTimeout(() => reject("promise2"), 3000)
);

const listOfPromise = [promise1, promise2];

Promise.allSettled(listOfPromise).then((results) => console.log(results)); 

// [{status: 'fulfilled', value: 'promise1'}, {status: 'rejected', reason: 'promise2'}]
Enter fullscreen mode Exit fullscreen mode

Promise.any()

Promise.any() takes an iterable of promises and returns the result of the promise which gets resolved as soon as possible. If none of the promises are resolved then it returns an Aggregate Error.

const promise1 = new Promise((resolve) =>
  setTimeout(() => resolve("promise1"), 5000)
);

const promise2 = new Promise((resolve) =>
  setTimeout(() => resolve("promise2"), 3000)
);

const listOfPromise = [promise1, promise2];

Promise.any(listOfPromise).then((result) => console.log(result));

// promise2 
Enter fullscreen mode Exit fullscreen mode

Promise.race()

Just like Promise.any() Promise.race() takes an iterable of promises and wait until any one of the promises is settled and return the result or error depending upon the condition.


const promise1 = new Promise((resolve) =>
  setTimeout(() => resolve("promise1"), 5000)
);

const promise2 = new Promise((resolve, reject) =>
  setTimeout(() => reject("promise2 rejected"), 3000)
);

const listOfPromise = [promise1, promise2];

Promise.race(listOfPromise)
  .then((result) => console.log(result))
  .catch((error) => console.log(error));

// promise2 rejected
Enter fullscreen mode Exit fullscreen mode

Promise.resolve()

The Promise.resolve(value) creates a resolved promise with the result of value. If the value is a promise then that promise is returned.

Promise.reject()

The Promise.reject(error) creates a new promise which is rejected with the given error.

These are the 6 static methods of promises.

That's all for this post, thank you for reading. I hope you learned something new.

If you have any questions, feel free to get in touch with me.

You can connect with me on

I will see you in the next post. Till then, keep coding :)

Top comments (3)

Collapse
 
lico profile image
SeongKuk Han

I didn't know allSettled, any and race, thank you for the good post!

Collapse
 
junaidshaikhjs profile image
Junaid Shaikh

Thank you for reading the post, SeongKuk.

I am glad that you liked it and learned something new.

Collapse
 
jwp profile image
John Peters

Nice article. Would you consider adding a section on using async/await?