Node JS Promises


Promises in Node.js are a powerful way to handle asynchronous operations, providing a more manageable and readable alternative to callbacks. They represent the eventual completion (or failure) of an asynchronous operation and its resulting value. Here's a comprehensive explanation:

1. What is a Promise?

A Promise is an object that represents the eventual completion or failure of an asynchronous operation. It allows you to write asynchronous code in a more synchronous-like manner, avoiding callback hell and making code easier to manage.

A Promise can be in one of three states:

  • Pending: The initial state; neither fulfilled nor rejected.
  • Fulfilled: The operation completed successfully.
  • Rejected: The operation failed with an error.

2. Creating a Promise

To create a Promise, use the Promise constructor, which takes an executor function. The executor function receives two arguments: resolve and reject.

Example:

const myPromise = new Promise((resolve, reject) => { // Simulate an asynchronous operation setTimeout(() => { const success = true; // Simulate success or failure if (success) { resolve('Operation successful'); } else { reject('Operation failed'); } }, 1000); });

In this example:

  • resolve is called when the operation completes successfully.
  • reject is called when the operation fails.

3. Using Promises

Once a Promise is created, you can handle its result using .then() and .catch() methods:

Example:

myPromise .then(result => { console.log(result); // Output: 'Operation successful' }) .catch(error => { console.error(error); // Output: 'Operation failed' });
  • .then(onFulfilled, onRejected): Handles the fulfillment or rejection of the Promise.
    • onFulfilled is called when the Promise is resolved.
    • onRejected is called when the Promise is rejected.
  • .catch(onRejected): Handles only the rejection of the Promise. It's a shorthand for .then(null, onRejected).

4. Chaining Promises

Promises can be chained to perform sequential asynchronous operations:

Example:

const doSomething = () => { return new Promise((resolve, reject) => { setTimeout(() => resolve('Step 1 complete'), 1000); }); }; const doSomethingElse = (message) => { return new Promise((resolve, reject) => { setTimeout(() => resolve(`${message} -> Step 2 complete`), 1000); }); }; doSomething() .then(result => { console.log(result); // Output: 'Step 1 complete' return doSomethingElse(result); }) .then(finalResult => { console.log(finalResult); // Output: 'Step 1 complete -> Step 2 complete' }) .catch(error => { console.error('Error:', error); });

In this example:

  • doSomething and doSomethingElse return Promises.
  • .then() is used to chain the operations and handle their results sequentially.
  • .catch() handles any errors that occur in the chain.

5. Promise.all()

Promise.all() is used to execute multiple Promises in parallel and wait for all of them to complete.

Example:

const promise1 = new Promise((resolve) => setTimeout(() => resolve('Result 1'), 1000)); const promise2 = new Promise((resolve) => setTimeout(() => resolve('Result 2'), 2000)); Promise.all([promise1, promise2]) .then(results => { console.log(results); // Output: ['Result 1', 'Result 2'] }) .catch(error => { console.error('Error:', error); });

In this example:

  • Promise.all() waits for all Promises to resolve and returns an array of results.
  • If any of the Promises reject, the returned Promise is rejected immediately with the reason of the first rejection.

6. Promise.race()

Promise.race() returns a Promise that resolves or rejects as soon as one of the Promises in the iterable resolves or rejects.

Example:

const promise1 = new Promise((resolve) => setTimeout(() => resolve('Result 1'), 1000)); const promise2 = new Promise((resolve) => setTimeout(() => resolve('Result 2'), 2000)); Promise.race([promise1, promise2]) .then(result => { console.log(result); // Output: 'Result 1' }) .catch(error => { console.error('Error:', error); });

In this example:

  • Promise.race() resolves with the result of the first Promise that completes, regardless of whether it resolves or rejects.

7. Promise.finally()

Promise.finally() is used to execute a cleanup action regardless of the Promise's outcome (whether it resolves or rejects).

Example:

const myPromise = new Promise((resolve, reject) => { setTimeout(() => resolve('Done'), 1000); }); myPromise .then(result => { console.log(result); // Output: 'Done' }) .catch(error => { console.error(error); }) .finally(() => { console.log('Cleanup or final actions'); // Output: 'Cleanup or final actions' });

Summary

  • Promises: Objects representing the eventual result of an asynchronous operation.
  • States: Pending, fulfilled, and rejected.
  • Methods: .then(), .catch(), .finally(), Promise.all(), Promise.race().
  • Advantages: More readable and manageable than nested callbacks, and easier to handle errors and multiple asynchronous operations.