Understanding Promises in Node.js

sharmila subbiah
3 min readJul 10, 2024

--

Promises are a powerful feature in Node.js, offering a more manageable way to handle asynchronous operations than traditional callbacks. This guide will delve into the concept of Promises, their benefits, and how to use them effectively with detailed examples.

What is a Promise?

A Promise is an object representing the eventual completion or failure of an asynchronous operation. It provides a more readable and maintainable approach to handle asynchronous code compared to callbacks, which often lead to “callback hell.”

A Promise has three states:

  • Pending: The initial state, neither fulfilled nor rejected.
  • Fulfilled: The operation completed successfully.
  • Rejected: The operation failed.

Creating a Promise

To create a Promise, use the Promise constructor, which takes a function with two arguments: resolve and reject. Here's a simple example:

const myPromise = new Promise((resolve, reject) => {
let success = true; // Simulating success or failure
if (success) {
resolve("Operation was successful!");
} else {
reject("Operation failed.");
}
});

myPromise.then(result => {
console.log(result); // "Operation was successful!"
}).catch(error => {
console.error(error); // "Operation failed."
});

Promises in Asynchronous Functions

Promises are particularly useful when dealing with asynchronous functions. Let’s look at an example using setTimeout to simulate an asynchronous operation:

const asyncOperation = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Asynchronous operation completed!");
}, 2000);
});
};

asyncOperation().then(result => {
console.log(result); // "Asynchronous operation completed!"
}).catch(error => {
console.error(error);
});

Chaining Promises

One of the significant advantages of Promises is the ability to chain them, allowing for sequential asynchronous operations. Here’s an example:

const firstOperation = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("First operation completed!");
}, 1000);
});
};

const secondOperation = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Second operation completed!");
}, 1000);
});
};

firstOperation().then(result => {
console.log(result); // "First operation completed!"
return secondOperation();
}).then(result => {
console.log(result); // "Second operation completed!"
}).catch(error => {
console.error(error);
});

Handling Multiple Promises

There are scenarios where you need to handle multiple Promises concurrently. Node.js provides methods like Promise.all and Promise.race

Promise.all

Promise.all takes an array of Promises and returns a single Promise that resolves when all of the input Promises resolve, or rejects if any of the input Promises reject.

const promise1 = Promise.resolve("Promise 1 resolved");
const promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, "Promise 2 resolved");
});
const promise3 = Promise.resolve("Promise 3 resolved");

Promise.all([promise1, promise2, promise3]).then(results => {
console.log(results); // ["Promise 1 resolved", "Promise 2 resolved", "Promise 3 resolved"]
}).catch(error => {
console.error(error);
});

Promise.race

Promise.race returns a Promise that resolves or rejects as soon as one of the input Promises resolves or rejects.

const promiseA = new Promise((resolve, reject) => {
setTimeout(resolve, 500, "Promise A resolved");
});
const promiseB = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, "Promise B resolved");
});

Promise.race([promiseA, promiseB]).then(result => {
console.log(result); // "Promise A resolved"
}).catch(error => {
console.error(error);
});

Error Handling with Promises

Handling errors in Promises is straightforward with the .catch method. If a Promise is rejected, the error can be caught and handled appropriately.

Promise.race([promiseA, promiseB]).then(result => {
console.log(result); // "Promise A resolved"
}).catch(error => {
console.error(error);
});

Converting Callbacks to Promises

Often, you may need to convert existing callback-based functions to use Promises. This can be achieved using the util.promisify method in Node.js.

const fs = require('fs');
const util = require('util');

const readFile = util.promisify(fs.readFile);

readFile('example.txt', 'utf8').then(data => {
console.log(data);
}).catch(error => {
console.error(error);
});

Conclusion

Promises offer a powerful way to handle asynchronous operations in Node.js, making your code cleaner and more manageable. By understanding and utilizing Promises, you can avoid the pitfalls of callback hell and write more robust, readable code.

--

--

sharmila subbiah

With over a decade of experience in the tech industry, I currently hold the position of Senior Software Engineer at Youlend.