Asynchronous Programming
Asynchronous programming lets a long-running operation run without blocking the main thread. Your program does not have to wait for the task to finish before it moves on. It starts the task, keeps running other code, and handles the result when the result is ready.
This matters in JavaScript because the language is single-threaded. It has one call stack and can only run one piece of code at a time. Without async programming, a network request or a file read would freeze the whole application until it finishes. In a browser, the UI stops responding. In Node.js, no other request can be served while that happens.
Here are the two approaches side by side:
import { readFile, readFileSync } from "node:fs";
// Synchronous: blocks until complete
const data = readFileSync("data.json", "utf8"); // Nothing else runs until this finishes
process(data);
// Asynchronous: non-blocking
readFile("data.json", "utf8", (error, data) => {
if (error) {
console.error("Read failed:", error.message);
return;
}
process(data); // Runs when the file is ready
});
doOtherWork(); // Runs immediately, doesn't wait for the file
JavaScript gets this async behavior from the event loop, which coordinates the call stack, the Web APIs (or the Node.js APIs), and the callback queues. The Preliminaries chapter already said that the JavaScript engine itself is synchronous. The runtime environment is what provides the async capabilities.
Learning Outcomes
- Explain why JavaScript needs asynchronous programming and how the event loop makes it possible
- Handle asynchronous operations using callbacks, Promises, and async/await
- Coordinate multiple concurrent operations with Promise combinators
- Apply error handling across callback-based, Promise-based, and async/await code
- Use the Fetch API to make and manage HTTP requests