JavaScript Async Programming
Understanding asynchronous programming concepts and patterns
⚡ What is Async Programming?
Asynchronous programming allows JavaScript to perform long-running operations without blocking the main thread. This enables responsive user interfaces and efficient handling of I/O operations.
// Synchronous (blocking)
console.log("Start");
console.log("Middle");
console.log("End");
// Asynchronous (non-blocking)
console.log("Start");
setTimeout(() => console.log("Async operation"), 1000);
console.log("End");
Output:
Start
End
Async operation (after 1 second)
Async Programming Concepts
Event Loop
Manages execution of async operations
console.log("1");
setTimeout(() => console.log("2"), 0);
console.log("3");
Callbacks
Functions passed to handle async results
function fetchData(callback) {
setTimeout(() => callback("data"), 1000);
}
Promises
Objects representing future values
const promise = new Promise((resolve) => {
setTimeout(() => resolve("done"), 1000);
});
Async/Await
Syntactic sugar for promises
async function getData() {
const result = await fetchData();
return result;
}
🔹 The Event Loop
Understanding how JavaScript handles asynchronous operations:
console.log("1: Synchronous");
setTimeout(() => {
console.log("2: Timeout (macro task)");
}, 0);
Promise.resolve().then(() => {
console.log("3: Promise (micro task)");
});
console.log("4: Synchronous");
// Execution order demonstrates event loop priority:
// 1. Synchronous code runs first
// 2. Micro tasks (Promises) run next
// 3. Macro tasks (setTimeout) run last
Output:
1: Synchronous
4: Synchronous
3: Promise (micro task)
2: Timeout (macro task)
🔹 Async Patterns Comparison
Different ways to handle the same async operation:
// 1. Callback Pattern
function fetchUserCallback(id, callback) {
setTimeout(() => {
const user = { id: id, name: "John Doe" };
callback(null, user);
}, 1000);
}
// 2. Promise Pattern
function fetchUserPromise(id) {
return new Promise((resolve) => {
setTimeout(() => {
const user = { id: id, name: "John Doe" };
resolve(user);
}, 1000);
});
}
// 3. Async/Await Pattern
async function fetchUserAsync(id) {
return new Promise((resolve) => {
setTimeout(() => {
const user = { id: id, name: "John Doe" };
resolve(user);
}, 1000);
});
}
// Usage examples
fetchUserCallback(1, (err, user) => console.log("Callback:", user));
fetchUserPromise(2).then(user => console.log("Promise:", user));
fetchUserAsync(3).then(user => console.log("Async:", user));
Output (after 1 second each):
Callback: {id: 1, name: "John Doe"}
Promise: {id: 2, name: "John Doe"}
Async: {id: 3, name: "John Doe"}
🔹 Error Handling in Async Code
Proper error handling for different async patterns:
// Promise error handling
function riskyOperation() {
return new Promise((resolve, reject) => {
const success = Math.random() > 0.5;
setTimeout(() => {
if (success) {
resolve("Operation successful!");
} else {
reject(new Error("Operation failed!"));
}
}, 1000);
});
}
// Using .catch()
riskyOperation()
.then(result => console.log("Success:", result))
.catch(error => console.log("Error:", error.message));
// Using async/await with try-catch
async function handleRiskyOperation() {
try {
const result = await riskyOperation();
console.log("Success:", result);
} catch (error) {
console.log("Error:", error.message);
}
}
handleRiskyOperation();
Output (randomly):
Success: Operation successful!
OR
Error: Operation failed!
🔹 Parallel vs Sequential Execution
Control the timing of multiple async operations:
// Sequential execution (one after another)
async function sequential() {
console.time("Sequential");
const result1 = await new Promise(resolve =>
setTimeout(() => resolve("Task 1"), 1000)
);
const result2 = await new Promise(resolve =>
setTimeout(() => resolve("Task 2"), 1000)
);
const result3 = await new Promise(resolve =>
setTimeout(() => resolve("Task 3"), 1000)
);
console.timeEnd("Sequential"); // ~3000ms
return [result1, result2, result3];
}
// Parallel execution (all at once)
async function parallel() {
console.time("Parallel");
const [result1, result2, result3] = await Promise.all([
new Promise(resolve => setTimeout(() => resolve("Task 1"), 1000)),
new Promise(resolve => setTimeout(() => resolve("Task 2"), 1000)),
new Promise(resolve => setTimeout(() => resolve("Task 3"), 1000))
]);
console.timeEnd("Parallel"); // ~1000ms
return [result1, result2, result3];
}
// Test both approaches
sequential().then(results => console.log("Sequential results:", results));
parallel().then(results => console.log("Parallel results:", results));
Output:
Sequential: 3000ms
Parallel: 1000ms
Sequential results: ["Task 1", "Task 2", "Task 3"]
Parallel results: ["Task 1", "Task 2", "Task 3"]