06 - Async JavaScript
What is Asynchronous JavaScript?
JavaScript is single-threaded, but it can handle tasks that take time (like fetching data from an API) without blocking other code from running.
console.log("Start");
setTimeout(() => {
console.log("This runs after 2 seconds");
}, 2000);
console.log("End");
// Output:
// Start
// End
// This runs after 2 secondsCallbacks
The original way to handle async operations:
function fetchData(callback) {
setTimeout(() => {
const data = { id: 1, name: "Alice" };
callback(data);
}, 1000);
}
fetchData((result) => {
console.log("Data received:", result);
});
// Real-world example — reading a file (Node.js)
const fs = require("fs");
fs.readFile("data.txt", "utf8", (error, data) => {
if (error) {
console.error("Error reading file:", error);
return;
}
console.log("File contents:", data);
});Callback Hell
Nested callbacks become hard to read:
// ❌ Callback hell — hard to read and maintain
getData((data) => {
processData(data, (processed) => {
saveData(processed, (saved) => {
sendEmail(saved, (sent) => {
console.log("All done!");
});
});
});
});Promises
A better way to handle async operations:
// Creating a promise
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = true;
if (success) {
resolve({ id: 1, name: "Alice" }); // success
} else {
reject("Something went wrong"); // failure
}
}, 1000);
});
}
// Using a promise
fetchData()
.then((data) => {
console.log("Success:", data);
})
.catch((error) => {
console.error("Error:", error);
});Promise States
A promise can be in one of three states:
// pending — initial state, not fulfilled or rejected
// fulfilled — operation completed successfully
// rejected — operation failed
const promise = new Promise((resolve, reject) => {
// Do async work here
// Call resolve(value) on success
// Call reject(error) on failure
});Chaining Promises
function getUser(id) {
return new Promise((resolve) => {
setTimeout(() => resolve({ id, name: "Alice" }), 1000);
});
}
function getPosts(userId) {
return new Promise((resolve) => {
setTimeout(() => resolve(["Post 1", "Post 2"]), 1000);
});
}
// ✅ Much cleaner than callbacks
getUser(1)
.then((user) => {
console.log("User:", user);
return getPosts(user.id);
})
.then((posts) => {
console.log("Posts:", posts);
})
.catch((error) => {
console.error("Error:", error);
});Promise Methods
// Promise.all — wait for all promises (fails if any fail)
const promise1 = fetch("https://api.example.com/users");
const promise2 = fetch("https://api.example.com/posts");
Promise.all([promise1, promise2])
.then(([users, posts]) => {
console.log("Both complete:", users, posts);
})
.catch((error) => {
console.error("One failed:", error);
});
// Promise.race — first one to finish wins
Promise.race([promise1, promise2])
.then((result) => {
console.log("First to finish:", result);
});
// Promise.allSettled — wait for all, even if some fail
Promise.allSettled([promise1, promise2])
.then((results) => {
results.forEach((result) => {
if (result.status === "fulfilled") {
console.log("Success:", result.value);
} else {
console.log("Failed:", result.reason);
}
});
});
// Promise.any — first success wins (ignores failures)
Promise.any([promise1, promise2])
.then((result) => {
console.log("First success:", result);
})
.catch(() => {
console.log("All failed");
});Async/Await
Modern, cleaner syntax for promises:
// Function must be marked as async
async function fetchUser() {
// await pauses execution until promise resolves
const response = await fetch("https://api.example.com/user");
const data = await response.json();
return data;
}
// Using async function
fetchUser()
.then((user) => console.log(user))
.catch((error) => console.error(error));
// Or with await in another async function
async function main() {
try {
const user = await fetchUser();
console.log("User:", user);
} catch (error) {
console.error("Error:", error);
}
}
main();Error Handling with Async/Await
async function fetchData() {
try {
const response = await fetch("https://api.example.com/data");
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error("Fetch failed:", error);
throw error; // re-throw if needed
}
}Sequential vs Parallel Execution
// ❌ Sequential — slow (3 seconds total)
async function slow() {
const user = await fetchUser(); // 1 second
const posts = await fetchPosts(); // 1 second
const comments = await fetchComments(); // 1 second
return { user, posts, comments };
}
// ✅ Parallel — fast (1 second total)
async function fast() {
const [user, posts, comments] = await Promise.all([
fetchUser(), // all start at the same time
fetchPosts(),
fetchComments()
]);
return { user, posts, comments };
}
// ✅ Parallel for independent tasks, sequential when one depends on another
async function smart() {
const user = await fetchUser(); // 1 second
const [posts, comments] = await Promise.all([
fetchPosts(user.id), // these run in parallel
fetchComments(user.id)
]);
return { user, posts, comments };
}Fetch API
Making HTTP requests in modern JavaScript:
// GET request
async function getUsers() {
const response = await fetch("https://api.example.com/users");
const data = await response.json();
return data;
}
// POST request
async function createUser(user) {
const response = await fetch("https://api.example.com/users", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(user)
});
const data = await response.json();
return data;
}
// Usage
createUser({ name: "Alice", email: "alice@example.com" })
.then((user) => console.log("Created:", user))
.catch((error) => console.error("Failed:", error));
// PUT request (update)
async function updateUser(id, updates) {
const response = await fetch(`https://api.example.com/users/${id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(updates)
});
return await response.json();
}
// DELETE request
async function deleteUser(id) {
const response = await fetch(`https://api.example.com/users/${id}`, {
method: "DELETE"
});
return response.ok;
}Practical Example — Loading Data
<!DOCTYPE html>
<html>
<body>
<div id="app">
<h1>User List</h1>
<button id="load">Load Users</button>
<div id="loading" style="display: none;">Loading...</div>
<ul id="users"></ul>
</div>
<script>
const loadBtn = document.getElementById("load");
const loading = document.getElementById("loading");
const usersList = document.getElementById("users");
loadBtn.addEventListener("click", loadUsers);
async function loadUsers() {
try {
// Show loading
loading.style.display = "block";
usersList.innerHTML = "";
// Fetch data
const response = await fetch("https://jsonplaceholder.typicode.com/users");
const users = await response.json();
// Hide loading
loading.style.display = "none";
// Display users
users.forEach((user) => {
const li = document.createElement("li");
li.textContent = `${user.name} (${user.email})`;
usersList.appendChild(li);
});
} catch (error) {
loading.style.display = "none";
usersList.innerHTML = `<li style="color: red;">Error: ${error.message}</li>`;
}
}
</script>
</body>
</html>setTimeout and setInterval
// setTimeout — run once after delay
const timeoutId = setTimeout(() => {
console.log("Runs after 2 seconds");
}, 2000);
// Cancel timeout
clearTimeout(timeoutId);
// setInterval — run repeatedly
const intervalId = setInterval(() => {
console.log("Runs every second");
}, 1000);
// Cancel interval
clearInterval(intervalId);
// Practical example — countdown timer
let count = 10;
const countdown = setInterval(() => {
console.log(count);
count--;
if (count < 0) {
clearInterval(countdown);
console.log("Done!");
}
}, 1000);Key Takeaways
- JavaScript can handle async operations without blocking
- Callbacks are the old way — can lead to callback hell
- Promises provide a cleaner way to handle async operations
async/awaitis the modern, most readable syntax- Use
try/catchwith async/await for error handling Promise.allruns promises in parallel for better performance- Fetch API is the modern way to make HTTP requests
- Always handle errors in async code
- Use
awaitonly insideasyncfunctions