JavaScript Promises and async/await Explained
Asynchronous JavaScript is tested in every senior interview. Here's what you need to know — from callbacks to async/await.
Callbacks and callback hell
The original pattern for async code. Deeply nested callbacks become hard to read — this is "callback hell".
// Callback style
fetchUser(userId, (user) => {
fetchPosts(user.id, (posts) => {
fetchComments(posts[0].id, (comments) => {
// deeply nested — hard to read and error-prone
console.log(comments);
});
});
});Promises
A Promise represents a value that will be available in the future. It can be pending, fulfilled, or rejected.
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
const success = true;
if (success) resolve("data");
else reject(new Error("failed"));
}, 1000);
});
promise
.then(data => console.log(data)) // "data"
.catch(err => console.error(err))
.finally(() => console.log("done")); // always runsasync/await
async/await is syntactic sugar over Promises. An async function always returns a Promise. await pauses execution until the Promise resolves.
async function getUser(id) {
try {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) throw new Error("Not found");
const user = await response.json();
return user;
} catch (err) {
console.error(err);
throw err; // re-throw so callers can handle it
}
}
// Calling an async function always returns a Promise:
getUser(1).then(user => console.log(user));Promise.all, Promise.race, Promise.allSettled
These combinators let you handle multiple Promises at once.
const p1 = fetch("/api/users");
const p2 = fetch("/api/posts");
const p3 = fetch("/api/comments");
// Wait for ALL — fails fast if any rejects
const [users, posts, comments] = await Promise.all([p1, p2, p3]);
// Wait for FIRST to settle
const first = await Promise.race([p1, p2, p3]);
// Wait for ALL, get results regardless of success/failure
const results = await Promise.allSettled([p1, p2, p3]);
// [{status: "fulfilled", value: ...}, {status: "rejected", reason: ...}]Common async pitfalls
These mistakes are tested in interviews.
// PITFALL 1: forgetting await
async function bad() {
const data = fetch("/api"); // returns Promise, not data!
console.log(data); // Promise object, not response
}
// PITFALL 2: sequential when parallel is possible
async function slow() {
const a = await fetchA(); // waits for A
const b = await fetchB(); // then waits for B
// Total time = A + B
}
async function fast() {
const [a, b] = await Promise.all([fetchA(), fetchB()]);
// Total time = max(A, B)
}Exam tip
The most common async question: "What's the difference between Promise.all and Promise.allSettled?" — all fails fast on first rejection; allSettled waits for everything and gives you each result.
Think you're ready? Prove it.
Take the free JavaScript readiness test. Get a score, topic breakdown, and your exact weak areas.
Take the free JavaScript test →Free · No sign-up · Instant results