Understanding Promise in JavaScript

Shameel Uddin - Aug 26 '23 - - Dev Community

As per MDN Web Docs, promise is an object representing the eventual completion or failure of an asynchronous operation.

Let’s make it a bit simpler.

Consider JavaScript promise like a real-world promise where JavaScript promises us that it will give us something after an asynchronous task has been completed.

Asynchronous operation/task can be one of the following:

  • Fetching data from server
  • Reading a file
  • Writing to a file
  • Reading/Writing to a database

What did promise solve?

Before promises, job of handling asynchronous tasks was handled by Callbacks solely but that approach created Pyramid of Doom or Callback Hell down the line, due to which the code becomes more of a spaghetti and nearly impossible to manage. You can check my previous article to know more about this in detail here.

How does Promise work?

We will break it down to two main concerns:

  1. Promise Creation.
  2. Promise Consumption.

Promise Creation:

You create a promise like this:

new Promise((resolve,reject)=>{})

It accepts a callback function with resolve and reject.

Here is a short snippet about how it works:

const myPromise = new Promise((resolve, reject) => {
  // Asynchronous operation, e.g., fetching data from a server
});
Enter fullscreen mode Exit fullscreen mode

If the code is successful, then call resolve else, call reject.

Emulating the example like this:

const fetchData = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const data = { id: 1, name: 'John' };
      // Simulate success
      // resolve(data); // Uncomment this line to resolve the promise

      // Simulate error
      reject(new Error('Failed to fetch data')); // Comment out if resolving
    }, 1000); // Simulated delay of 1 second
  });
};
Enter fullscreen mode Exit fullscreen mode

We used setTimeout to emulate a delay in response of fetching the data.

A somewhat real world example of creating a promise is like this:

const authenticateUser = (username, password) => {
  return new Promise((resolve, reject) => {
    // Simulate authentication by checking against hardcoded values
    const validUsername = 'user123';
    const validPassword = 'pass456';

    setTimeout(() => {
      if (username === validUsername && password === validPassword) {
        resolve('Authentication successful');
      } else {
        reject(new Error('Authentication failed'));
      }
    }, 1000); // Simulated delay of 1 second
  });
};

Enter fullscreen mode Exit fullscreen mode

What we have done is created an arrow function authenticationUser which accepts username and password which returns a promise. If you enter valid username and password as described in the code user123 and pass456 then it will return you Authentication successful otherwise it will return Authentication failed.

We learned how to create the promise. We will now move forward to the consumption of the promise.

Promise Consumption:

Consumption of a promise require then for resolving and catch for rejection of a promise.

We can call fetchData that we made earlier like this:

fetchData()
  .then(result => {
    console.log('Data fetched:', result);
  })
  .catch(error => {
    console.error('Error:', error.message);
  });
Enter fullscreen mode Exit fullscreen mode

You can consume authenticateUser promise like this:


// Usage
authenticateUser('user123', 'pass456')
  .then(message => {
    console.log(message);
  })
  .catch(error => {
    console.error('Error:', error.message);
  });
Enter fullscreen mode Exit fullscreen mode

Check it out directly in the browser. Open console and then paste the above snippet:

Image description

You can see in browser console that, at first the state of promise appeared to be pending and after it was resolved, you saw the result.
You can try this with wrong username and password at your end to test it out.

Promise .finally Method:

This method is always executed whether the promise is resolved or rejected.

Code:

I urge you to try these examples directly in your browser to check them out quickly if you do not have JS environment up and running.

function simulateAsyncOperation(success) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (success) {
        resolve("Operation succeeded!");
      } else {
        reject(new Error("Operation failed."));
      }
    }, 1000);
  });
}

function performOperation(success) {
  simulateAsyncOperation(success)
    .then(result => {
      console.log("Resolved:", result);
    })
    .catch(error => {
      console.error("Rejected:", error.message);
    })
    .finally(() => {
      console.log("Finally block executed.");
    });
}

Enter fullscreen mode Exit fullscreen mode

Image description

// Call the function with success set to true for resolved path
performOperation(true);
Enter fullscreen mode Exit fullscreen mode

Image description

// Call the function with success set to false for rejected path
performOperation(false);
Enter fullscreen mode Exit fullscreen mode

Image description

You can further check the process of finally with below textual diagrams.

Resolved to Finally:

   +-------------------------+
   |                         |
   |     Initial State       |
   |                         |
   +-------------------------+
               |
               | Promise is
               | created
               |
               v
   +-------------------------+
   |                         |
   |   Pending State         |
   |                         |
   +-------------------------+
               |
               | Asynchronous
               | operation
               |
               v
   +-------------------------+
   |                         |
   |   Fulfilled State       |
   |  (Resolve Callback)     |
   +-------------------------+
               |
               | Promise is
               | resolved
               |
               v
   +-------------------------+
   |                         |
   |    Finally Block        |
   |    (Always Executed)    |
   +-------------------------+
Enter fullscreen mode Exit fullscreen mode

Rejected to Finally:

   +-------------------------+
   |                         |
   |     Initial State       |
   |                         |
   +-------------------------+
               |
               | Promise is
               | created
               |
               v
   +-------------------------+
   |                         |
   |   Pending State         |
   |                         |
   +-------------------------+
               |
               | Asynchronous
               | operation
               |
               v
   +-------------------------+
   |                         |
   |    Rejected State       |
   |   (Reject Callback)     |
   +-------------------------+
               |
               | Promise is
               | rejected
               |
               v
   +-------------------------+
   |                         |
   |    Finally Block        |
   |    (Always Executed)    |
   +-------------------------+
Enter fullscreen mode Exit fullscreen mode

Promise Chaining:

Promise chaining allows you to execute multiple asynchronous operations in sequence, where the result of one operation becomes the input for the next operation. This is a way to solve Callback Hell.

Let’s take this example:

function asyncOperation(data) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (data) {
        resolve(data.toUpperCase());
      } else {
        reject("No data provided");
      }
    }, 1000);
  });
}
Enter fullscreen mode Exit fullscreen mode

This is how we can perform Promise Chaining i.e., by feeding the returning a promise in then statement.

asyncOperation("shameel")
  .then(result => {
    console.log(result); // Output: "SHAMEEL"
    return asyncOperation("uddin");
  })
  .then(result => {
    console.log(result); // Output: "UDDIN"
  })
  .catch(error => {
    console.error(error);
  });
Enter fullscreen mode Exit fullscreen mode

We thoroughly discussed it here.

Conclusion

We learned how to create a promise as well as how to consume a promise in this blog and associated methods with it.

What’s Next?

In the next blog, we will discover and deep dive into various promise methods. Understanding and utilizing these methods will enable you to effectively manage asynchronous operations and build more robust and maintainable code using promises.

Follow me here for more stuff like this:
LinkedIn: https://www.linkedin.com/in/shameeluddin/
Github: https://github.com/Shameel123

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player