Promises

Node.js Promises

In the previous tutorial, we learned about callbacks and the dreaded "Callback Hell". To solve the problem of deeply nested callbacks, JavaScript introduced Promises.

Promises are a much cleaner, more elegant way to handle asynchronous operations in Node.js. They provide better readability and superior error handling.


1. What is a Promise?

Imagine you order a coffee at a busy cafe. The cashier takes your money and hands you a receipt with a ticket number. That receipt is a Promise. It is a guarantee that eventually, you will receive either your coffee or a reason why they cannot fulfill your order (e.g., they ran out of milk).

In programming, a Promise is an object that represents the eventual completion (or failure) of an asynchronous operation and its resulting value.

The Three States of a Promise

A Promise can only be in one of three states at any given time:

  1. Pending: The initial state. The operation has not completed yet (you are waiting for your coffee).
  2. Fulfilled (Resolved): The operation completed successfully (you got your coffee!).
  3. Rejected: The operation failed, and an error is returned (they ran out of milk).

2. Creating a Promise

You can create a new Promise using the new Promise constructor. It takes a function with two arguments: resolve and reject.

Creating a Promise Example

const myPromise = new Promise((resolve, reject) => {
  let success = true; // Change this to false to see the rejection!

setTimeout(() => { if (success) { resolve("Operation was a success! Here is your data."); } else { reject("Operation failed! Something went wrong."); } }, 2000); // Simulating a 2-second delay });

myPromise .then((message) => { console.log(message); }) .catch((error) => { console.error(error); });


3. Consuming a Promise (.then and .catch)

Creating a promise is only half the battle. Once you have a promise, you need to "consume" or handle its result. We do this using .then() for successes and .catch() for errors.

Consuming the Promise

const myPromise = Promise.resolve("Operation was a success! Here is your data.");

myPromise .then((message) => { // This runs if resolve() was called console.log("Success:", message); }) .catch((error) => { // This runs if reject() was called console.error("Error:", error); }) .finally(() => { // This runs regardless of success or failure console.log("Promise processing is finished."); });

The .finally() block is optional but very useful for running cleanup code (like closing a loading spinner or a database connection), regardless of whether the promise was resolved or rejected.


4. Chaining Promises

The biggest advantage of Promises over raw callbacks is chaining. Instead of nesting code deeper and deeper, you can chain multiple .then() methods.

When you return a value inside a .then(), it automatically gets wrapped in a new Promise, allowing the next .then() to receive it.

Promise Chaining Example

const fetchNumber = new Promise((resolve, reject) => resolve(10));

fetchNumber .then((num) => { console.log("Started with:", num); return num * 2; // Returns 20 to the next .then() }) .then((num) => { console.log("Multiplied by 2:", num); return num * 5; // Returns 100 to the next .then() }) .then((finalNum) => { console.log("Final result:", finalNum); }) .catch((err) => { // If ANY of the promises above fail, it instantly jumps here! console.error("Caught an error in the chain:", err); });

Notice how much cleaner this is! The code reads from top to bottom, rather than diagonally inwards like Callback Hell. Furthermore, a single .catch() at the end will catch errors from any step in the chain.


5. Built-in Node.js Promises

Modern Node.js comes with Promise-based versions of its core modules, so you rarely have to create your own wrappers. For example, instead of using the callback-based fs.readFile, you can use fs.promises.

const fs = require('fs').promises;

fs.readFile('example.txt', 'utf8') .then((data) => { console.log("File content:", data); }) .catch((err) => { console.error("Could not read file:", err.message); });


Best Practices for Promises


Exercise

?

Which of the following is NOT a valid state for a Promise?