You are viewing a free preview of this lesson.
Subscribe to unlock all 10 lessons in this course and every other course on LearningBro.
Asynchronous code is at the core of Node.js. The platform was designed to handle I/O without blocking, and over time JavaScript has given developers progressively better tools for writing async code.
The original Node.js pattern is the error-first callback: a function passed as the last argument that Node.js calls when the async operation completes. The first parameter is always an error (or null on success):
const fs = require("fs");
fs.readFile("data.txt", "utf8", function (err, data) {
if (err) {
console.error("Failed:", err.message);
return;
}
console.log(data);
});
When callbacks are nested, the result is sometimes called callback hell:
fs.readFile("a.txt", "utf8", function (err, a) {
fs.readFile("b.txt", "utf8", function (err, b) {
fs.writeFile("c.txt", a + b, "utf8", function (err) {
console.log("Done");
});
});
});
A Promise represents a value that will be available in the future. It is either pending, fulfilled (resolved), or rejected:
const fs = require("fs/promises");
fs.readFile("data.txt", "utf8")
.then(function (data) {
console.log(data);
return fs.writeFile("copy.txt", data, "utf8");
})
.then(function () {
console.log("Copy written");
})
.catch(function (err) {
console.error("Error:", err.message);
});
Promises chain cleanly with .then() and handle errors centrally with .catch(), eliminating deep nesting.
async/await is syntactic sugar over Promises that makes async code read like synchronous code:
const fs = require("fs/promises");
async function copyFile(src, dest) {
try {
const data = await fs.readFile(src, "utf8");
await fs.writeFile(dest, data, "utf8");
console.log("Copy complete");
} catch (err) {
console.error("Error:", err.message);
}
}
copyFile("data.txt", "copy.txt");
Marking a function with async causes it to always return a Promise. The await keyword pauses execution inside that function until the awaited Promise settles, without blocking the event loop.
When operations are independent, run them simultaneously with Promise.all():
const fs = require("fs/promises");
async function readAll() {
const [a, b, c] = await Promise.all([
fs.readFile("a.txt", "utf8"),
fs.readFile("b.txt", "utf8"),
fs.readFile("c.txt", "utf8"),
]);
console.log(a, b, c);
}
readAll();
Promise.all() rejects immediately if any promise rejects. Use Promise.allSettled() if you need all results regardless of individual failures.
| Pattern | Use When |
|---|---|
| Callbacks | Working with older Node.js APIs or libraries |
| Promises | Chaining sequential async steps |
| async/await | Writing readable, maintainable async code (preferred) |
| Promise.all() | Running independent async tasks in parallel |
Subscribe to continue reading
Get full access to this lesson and all 10 lessons in this course.