Now let’s cover Promises and async/await in Node.js, which are essential for handling asynchronous operations cleanly. We’ll go step by step and tie it to the Node environment.


1️⃣ What Are Promises?

  • Promise = an object representing the eventual completion or failure of an asynchronous operation.

  • Avoids “callback hell” by chaining .then() and .catch().

States of a Promise

  1. Pending → initial state

  2. Fulfilled → operation completed successfully

  3. Rejected → operation failed


2️⃣ Basic Promise Example in Node.js

const fs = require("fs").promises; // fs.promises for async file ops
 
fs.readFile("example.txt", "utf8")
  .then((data) => {
    console.log("File contents:", data);
  })
  .catch((err) => {
    console.error("Error reading file:", err);
  });
  • fs.promises.readFile returns a Promise

  • .then() handles success, .catch() handles errors


3️⃣ async/await Syntax

  • Cleaner syntax for Promises

  • Allows writing asynchronous code as if it were synchronous

const readFileAsync = async () => {
  try {
    const data = await fs.readFile("example.txt", "utf8");
    console.log("File contents:", data);
  } catch (err) {
    console.error("Error reading file:", err);
  }
};
 
readFileAsync();
  • async → marks function as asynchronous

  • await → pauses function execution until the Promise resolves/rejects


4️⃣ Multiple Async Operations

Sequential Execution

const sequential = async () => {
  const data1 = await fs.readFile("file1.txt", "utf8");
  const data2 = await fs.readFile("file2.txt", "utf8");
  console.log(data1, data2);
};

Parallel Execution

const parallel = async () => {
  const [data1, data2] = await Promise.all([
    fs.readFile("file1.txt", "utf8"),
    fs.readFile("file2.txt", "utf8")
  ]);
  console.log(data1, data2);
};
  • Use Promise.all for parallel async operations → faster than sequential

5️⃣ Error Handling

const fetchData = async () => {
  try {
    const data = await someAsyncFunction();
    console.log(data);
  } catch (err) {
    console.error("Something went wrong:", err);
  }
};
  • Always wrap await calls in try/catch or .catch() to prevent unhandled rejections

6️⃣ Node-Specific Examples

  1. Database Queries (Mongoose / Sequelize)
const getUsers = async () => {
  const users = await User.find(); // Mongoose
  // or await User.findAll(); // Sequelize
  return users;
};
  1. HTTP Requests (Axios)
const axios = require("axios");
 
const fetchData = async () => {
  try {
    const response = await axios.get("https://jsonplaceholder.typicode.com/posts");
    console.log(response.data);
  } catch (err) {
    console.error(err);
  }
};

7️⃣ Key Takeaways

  • Promises → manage async operations, chain .then()/.catch()

  • async/await → cleaner syntax, easier to read, reduces callback nesting

  • Use Promise.all for parallel operations

  • Always handle errors with try/catch or .catch()

  • Works seamlessly with Node.js I/O, DB calls, APIs