How does JavaScript handle asynchronous operations?
Introduction
JavaScript is single-threaded but handles asynchronous operations efficiently through its Event Loop and task queue. Understanding this mechanism is crucial for interviews and building performant apps.
1. The Problem: JavaScript’s Single-Threaded Nature
- JS runs on one thread, blocking long tasks (e.g., API calls).
- Solution: Async operations allow non-blocking execution.
2. How JavaScript Handles Async: The Event Loop
- Call Stack: Tracks function execution (LIFO).
- Web APIs: Browser features like
setTimeout or fetch.
- Callback Queue: Holds async callbacks (e.g.,
setTimeout).
- Event Loop: Checks the call stack; if empty, moves callbacks from the queue to the stack.
Example:
console.log("Start");
setTimeout(() => console.log("Timeout"), 0);
console.log("End");
// Output: Start → End → Timeout
3. Async Patterns
a. Callbacks
- Traditional method (e.g.,
fs.readFile).
- Drawback: "Callback Hell" (nested, hard-to-read code).
b. Promises
- Chainable with
.then() and .catch().
- States: Pending, Fulfilled, Rejected.
Example:
fetch('url')
.then(response => response.json())
.catch(error => console.log(error));
c. Async/Await
- Syntactic sugar for Promises (uses
async/await).
- Cleaner, synchronous-like code.
Example:
async function getData() {
try {
const response = await fetch('url');
console.log(await response.json());
} catch (error) {
console.log(error);
}
}
4. Key Interview Questions
- "Explain the Event Loop."
- "What’s the difference between Promises and Callbacks?"
- "How does
setTimeout(fn, 0) work?"
5. Best Practices
- Avoid callback hell with Promises/Async-Await.
- Use
Promise.all() for parallel tasks.
- Handle errors with
.catch() or try/catch.