Asynchronous Node

Nearly everything Node does that touches a disk or a network is asynchronous by default. You've already seen the callback style — this lesson covers the two patterns built on top of it that make async code far easier to read.

The callback style, revisited

The classic Node pattern hands a function an "error-first" callback — the first argument is either an error or null:

JS callback-style.js
const fs = require("fs");

fs.readFile("data.txt", "utf8", (err, data) => {
  if (err) {
    console.error("Failed:", err.message);
    return;
  }
  console.log("Got:", data);
});

This works fine for one operation. Chain three or four dependent reads together and it turns into deeply nested "callback hell" — which is exactly what Promises were built to fix.

Promises with fs.promises

Node's built-in modules ship promise-based versions alongside the callback ones:

JS promise-style.js
const fs = require("fs/promises");

fs.readFile("data.txt", "utf8")
  .then((data) => console.log("Got:", data))
  .catch((err) => console.error("Failed:", err.message));

async/await

async/await is syntax sugar over Promises, letting asynchronous code read top-to-bottom like synchronous code:

JS async-style.js
const fs = require("fs/promises");

async function loadData() {
  try {
    const data = await fs.readFile("data.txt", "utf8");
    console.log("Got:", data);
  } catch (err) {
    console.error("Failed:", err.message);
  }
}

loadData();
Output (assuming data.txt exists)
Got: some file contents

All three versions do the same thing — read a file without blocking the rest of the program — but async/await is what most modern Node code reaches for, because a try/catch block is a lot easier to follow than a chain of .then() calls once more than one async step is involved.

Note: since Node 15, an unhandled promise rejection — a rejected promise with no .catch() and no surrounding try/catch on an await — crashes the entire process by default, rather than failing silently. Always handle the error case on any promise you don't immediately await inside a try block.