What is a promise?

A promise is an object that represents the eventual completion (or failure) of an asynchronous operation and its resulting value. It allows you to write asynchronous code in a more manageable way, avoiding callback hell.

How does a promise work?

A promise has three states:

  1. Pending: The initial state, neither fulfilled nor rejected.
  2. Fulfilled: The operation completed successfully, and the promise has a resulting value.
  3. Rejected: The operation failed, and the promise has a reason for the failure (an error).

When a promise is fulfilled or rejected, it can trigger callbacks that were registered to handle those outcomes.

Creating a promise

You can create a promise using the Promise constructor. Here's the basic structure of a promise:

/*
resolve and reject are callback functions.
On success(fulfillment), run resolve() with the result. The result can be retrieved later in the `then` method.
On failure(rejection), run reject() with an error. The error can be retrieved later in the `catch` method.
*/
const myPromise = new Promise((resolve, reject) => {
    const success = true; // Simulate success or failure
    if (success) {
        resolve("Promise resolved successfully!");
    } else {
        reject("Promise failed!");
    }
});

Using promise

With the then and catch methods

Once you have created a promise, you can use the then and catch methods to handle the result of the promise.

  • The then method is called when the promise is fulfilled,
  • and the catch method is called when the promise is rejected.
// I used myResult and myError to prove that names can be changed to whatever you want.
myPromise
    .then((myResult) => {
        console.log(myResult); // "Promise resolved successfully!"
    })
    .catch((myError) => {
        console.error(myError); // "Promise failed!"
    })
    .finally(() => {
        console.log("Promise has settled (either resolved or rejected).");
    });    

Why use finally()?

The finally() method is useful for cleanup tasks that need to run regardless of the promise's outcome, such as:

  • Hiding a loading spinner.
  • Closing a database connection.
  • Logging that an operation has completed.

With async/await

You can also use async/await syntax to work with promises, which makes the code look more synchronous:

async function main() {
    try {
        const result = await myPromise;
        console.log(result);    // "Promise resolved successfully!"
    } catch (error) {
        console.error(error);   // "Promise failed!"
    }
}
main();

Calling asynchronous function inside a promise

Calling fetch() using then and catch method

const myPromiseFetch1 = new Promise((resolve, reject) => {
    const url = "https://jsonplaceholder.typicode.com/posts/1"; // Example API endpoint

    fetch(url)
        .then((response) => {
            if (!response.ok) {
                throw new Error(`HTTP error! status: ${response.status}`);
            }
            return response.json(); // Parse JSON response
        })
        .then((data) => {
            resolve(data); // Resolve the promise with the fetched data
        })
        .catch((error) => {
            reject(error); // Reject the promise with the error
        });
});

myPromiseFetch1
    .then((data) => {
        console.log("Fetched Data:", data); // Output the fetched data
    })
    .catch((error) => {
        console.error("Fetch Error:", error); // Output the error
    })
    .finally(() => {
        console.log("Fetch operation completed.");
    });

Calling fetch() using async/await

const myPromiseFetch2 = new Promise(async (resolve, reject) => {
    const url = "https://jsonplaceholder.typicode.com/posts/1"; // Example API endpoint

    try {
        const response = await fetch(url); // Await the fetch call
        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }
        const data = await response.json(); // Await the JSON parsing
        resolve(data); // Resolve the promise with the fetched data
    } catch (error) {
        reject(error); // Reject the promise with the error
    }
});

myPromiseFetch2
    .then((data) => {
        console.log("Fetched Data:", data); // Output the fetched data
    })
    .catch((error) => {
        console.error("Fetch Error:", error); // Output the error
    })
    .finally(() => {
        console.log("Fetch operation completed.");
    });

Chaining promises

You can chain multiple promises together using the then method. Each then returns a new promise, allowing you to perform additional asynchronous operations in sequence:

myPromise
  .then((result) => {
    console.log(result);
    return new Promise((resolve) => {
      setTimeout(() => resolve("Second operation completed!"), 1000);
    });
  })
  .then((secondResult) => {
    console.log(secondResult); // "Second operation completed!"
  })
  .catch((error) => {
    console.error(error.message);
  });

Promise.all

You can use Promise.all to run multiple promises in parallel and wait for all of them to complete:

const promise1 = new Promise((resolve) => setTimeout(() => resolve("First"), 1000));
const promise2 = new Promise((resolve) => setTimeout(() => resolve("Second"), 2000));
const promise3 = new Promise((resolve) => setTimeout(() => resolve("Third"), 1500));
Promise.all([promise1, promise2, promise3])
  .then((results) => {
    console.log(results); // ["First", "Second", "Third"]
  })
  .catch((error) => {
    console.error(error.message);
  });