Advance JavaScript (DOM, BOM, Web API) / Asynchronous JavaScript

Promises

The Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value. A promise object has a state that can be one of the following:

  • Pending

  • Fulfilled with a value

  • Rejected for a reason

Lets Understand these states in details:

  • In the beginning, the state of a promise is pending, indicating that the asynchronous operation is in progress( the result is undefined). Depending on the result of the asynchronous operation, the state changes to either fulfilled or rejected.

The fulfilled state indicates that the asynchronous operation was completed successfully.

The rejected state indicates that the asynchronous operation failed.


🪧 Creating a promise

To create a promise object, you use the Promise() constructor:

const promise = new Promise((resolve, reject) => {
  // contain an operation
  // ...

  // return the state
  if (success) {
    resolve(value);
  } else {
    reject(error);
  }
});
  • The Promise() constructor takes a function called the executor.

  • The executor receives two functions:

    • resolve() → call this when things go right

    • reject() → call this when something goes wrong

  • These names (resolve, reject) are just convention, you can name them anything.


  • If the async task succeeds, the executor calls resolve() → promise becomes fulfilled.

  • If the task fails, it calls reject() → promise becomes rejected.

  • Once a promise is fulfilled or rejected, its state cannot change.

  • It can’t switch between fulfilled ↔ rejected, and can’t go back to pending.

  • Every promise starts as pending, and once it becomes fulfilled or rejected, it is considered resolved.


🧩 Consuming a Promise: then, catch, finally

1. The then( ) method

To get the value of a promise when it’s fulfilled, you call the then() method of the promise object. The following shows the syntax of the then() method:

promise.then(onFulfilled,onRejected);

→ The then() method accepts two callback functions: onFulfilled and onRejected.

Example :

// A function that returns users after 1 second
function getUsers() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve([
        { username: "john", email: "john@test.com" },
        { username: "jane", email: "jane@test.com" }
      ]);
    }, 1000);
  });
}

// Use the promise
getUsers().then((users) => {
  console.log(users);
});
[
	{ username: 'john', email: 'john@test.com'},
	{ username: 'jane', email: 'jane@test.com}
]
  • You call getUsers().

  • This immediately returns a Promise (not the users yet).

  • JS continues running without waiting (because promises are async).

  • After 1 second, the promise resolves with the users array.

  • .then() waits for the promise to resolve.

  • When the promise resolves, the callback inside .then() runs.

  • That callback receives the users and prints them.


2. The catch( ) method

If you want to get the error only when the state of the promise is rejected, you can use the catch() method of the Promise object:

promise.catch(onRejected);

→ Internally, the catch() method invokes the then(**undefined**, **onRejected**) method.

Example :

The following example changes the success flag to false to simulate the error scenario:

let success = false;

function getUsers() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      success
        ? resolve([
            { username: "john", email: "john@test.com" },
            { username: "jane", email: "jane@test.com" }
          ])
        : reject("Failed to fetch the user list");
    }, 1000);
  });
}

getUsers().catch((error) => console.log(error));

❌ If success = false

Failed to fetch the user list

✅ If success = true

[
	{ username: 'john', email: 'john@test.com'},
	{ username: 'jane', email: 'jane@test.com}
]
  • getUsers() returns a Promise.

  • After 1 second, it checks the success variable.

  • If success is true → the promise resolves (gives user data).

  • If success is false → the promise rejects (throws an error).

  • .catch() receives that error and prints it.


3) The finally( ) method

Sometimes, you want to execute the same piece of code whether the promise is fulfilled or rejected.

  • Removes duplication

  • Improves readability

  • Separates concerns cleanly

Example:

let success = false;

function getUsers() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      success
        ? resolve("User data fetched successfully")
        : reject("Failed to fetch the user list");
    }, 1000);
  });
}

getUsers()
  .then((data) => console.log(data))
  .catch((error) => console.log(error))
  .finally(() => console.log("Task completed"));
User data fetched successfully
Task completed
  • Runs whether the promise succeeds or fails.

  • Useful for cleanup tasks (loading stop, closing spinner, etc.).


🔗 Promise Chaining

Chain multiple .then() calls to handle sequential operations. Return values flow through the chain, and errors propagate.

The following picture illustrates a promise that has multiple handlers .

1️⃣ Syntax

step1()
.then(result => step2(result))
.then(result => step3(result))
...

2️⃣ Example


🌀Promise.all( ) method

The Promise.all() static method takes an iterable of promises:

🚩Syntax

Promise.all(iterable);

The Promise.all() method returns a single promise that resolves when all the input promises have been resolved. The returned promise resolves to an array of the results of the input promises:

  • promise1 resolves to value v1 at time t1.

  • promise2 resolves to value v2 at time t2.

  • Since Promise.all() waits for all promises to resolve:

    • It does not resolve at t1, even if promise1 is already done.

    • It waits until the last promise (promise2) resolves at t2.

  • At time t2, both promise1 and promise2 have resolved.

  • Therefore:

    • Promise.all([promise1, promise2]) resolves at t2.

    • It returns an array with both values:

      👉 [v1, v2]


➡️ If one of the input promises is rejected, the Promise.all() method immediately returns a promise that is rejected with an error of the first rejected promise:

  • Suppose promise2 rejects at time t1 with an error.

  • Promise.all([promise1, promise2]) will:

    ✅ Immediately reject at t1 with the same error from promise2.

  • Promise.all():

    • Does not wait for promise1 or other promises to resolve.

    • Ignores all remaining input promises once any one rejects.

  • Even if:

    • promise1 is still pending,

    • or it eventually resolves/rejects,

    • their results are discarded.


♨️Promise.all( ) method examples

Let’s take some examples to understand how the Promise.all() method works.

1) Resolved promises example

The following promises resolve to 10, 20, and 30 after 1, 2, and 3 seconds. We use the setTimeout() to simulate the asynchronous operations:

  • When all promises have been resolved, the values from these promises are passed into the callback of the then() method as an array.

  • Inside the callback, we use the Array’s reduce() method to calculate the total value and use the console.log to display the array of values as well as the total.


2) Rejected promises example

The Promise.all() returns a Promise that is rejected if any of the input promises are rejected.

  • There are three promises:

    • ✅ The first promise resolves after 1 second

    • ❌ The second promise rejects after 2 seconds

    • ✅ The third promise resolves after 3 seconds

  • You use Promise.all([p1, p2, p3]) to run them together.

  • Promise.all():

    • Fails immediately when any one promise is rejected.

    • So, when the second promise rejects after 2 seconds:

      • The entire Promise.all() rejects.

      • The .catch() block runs.

  • Even though the other promises may eventually resolve, their results are ignored.