Node.js Tutorial menu

Node.js Asynchronous Programming

Most work in Node.js, like reading files or calling APIs, is asynchronous: it starts now and finishes later.

Synchronous vs Asynchronous

Synchronous code runs line by line, and each line waits for the one before it. Asynchronous code starts a task and continues without waiting. When the task is done, your code is told about the result.

Example

js
console.log("Order placed");

setTimeout(() => {
  console.log("Pizza delivered (after 2 seconds)");
}, 2000);

console.log("Watching TV while waiting");
Output
Order placed
Watching TV while waiting
Pizza delivered (after 2 seconds)

Callbacks

The oldest style is the callback: a function you pass in, which runs when the task finishes. Node.js callbacks are error-first: the first argument is an error (or null), and the second is the result.

Example

js
const fs = require("fs");

fs.readFile("students.txt", "utf8", (err, data) => {
  if (err) {
    console.error("Could not read file:", err.message);
    return;
  }
  console.log("File content:", data);
});

When many callbacks are nested inside each other, code becomes hard to read. People call this callback hell.

Promises

A promise is an object that stands for a result that will arrive later. It is in one of three states:

StateMeaning
PendingStill working
FulfilledFinished with a value
RejectedFailed with an error

Use .then() for the result and .catch() for errors:

Example

js
import { readFile } from "node:fs/promises";

readFile("students.txt", "utf8")
  .then((data) => console.log("Content:", data))
  .catch((err) => console.error("Error:", err.message));

You can also create your own promise:

Example

js
function checkFee(paid) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (paid) resolve("Fee received, welcome to class!");
      else reject(new Error("Fee is pending"));
    }, 1000);
  });
}

checkFee(true).then((msg) => console.log(msg));
Output
Fee received, welcome to class!

async and await

async/await lets you write promise code that reads like normal, top-to-bottom code. This is the style most modern Node.js code uses.

Example

js
import { readFile } from "node:fs/promises";

async function showStudents() {
  try {
    const data = await readFile("students.txt", "utf8");
    console.log("Students:");
    console.log(data);
  } catch (err) {
    console.error("Something went wrong:", err.message);
  }
}

showStudents();
Output
Students:
Ali
Ayesha
Hamza

Rules to remember:

  • await pauses only this function until the promise finishes. Node.js keeps doing other work
  • You can use await inside an async function, or at the top level of an ES module
  • Use try/catch to handle errors from awaited promises

Running Tasks Together

If tasks do not depend on each other, start them all at once with Promise.all. It is faster than awaiting them one by one.

Example

js
import { readFile } from "node:fs/promises";

const [boys, girls] = await Promise.all([
  readFile("boys.txt", "utf8"),
  readFile("girls.txt", "utf8"),
]);

console.log("Both lists are loaded");

If any of the promises fails, Promise.all fails too. Use Promise.allSettled when you want every result, even if some fail.

A Small Helper: sleep

Example

js
import { setTimeout as sleep } from "node:timers/promises";

console.log("Starting backup...");
await sleep(1500);
console.log("Backup finished");
Output
Starting backup...
Backup finished

Forgetting await is a very common bug. Without it, you get a Promise { <pending> } object instead of the data. If you see that in your output, look for a missing await.

Exercise

Exercise

What does the await keyword do?

Learn Node.js with a teacher

Join the Full Stack Development course at Workownix, online or in Orangi Town, Karachi, and get a certificate.

See the course Ask on WhatsApp