Async/Await & Promises
Handling Work That Takes Time
Open interactive version (quiz + challenge)Real-world analogy
A Promise is like ordering pizza. You get a receipt (Promise) immediately. Pizza isn't ready yet — it's PENDING. Later: Pizza arrives (FULFILLED) or they call saying it's cancelled (REJECTED). async/await is a cleaner way to handle pizza than the old .then() way!
What is it?
Promises represent values that don't exist yet but will in the future. async/await is syntax sugar that makes Promises feel like normal code. It's the standard way to handle asynchronous work (API calls, file reads, timeouts).
Real-world relevance
Every API call is a Promise. React Query wraps Promises. You'll use async/await multiple times every day!
Key points
- Promise States: Pending → Fulfilled → Rejected — PENDING: waiting. FULFILLED: got the value. REJECTED: error happened. A Promise is always in one of these states.
- async/await — The Modern Way — async function getName() { const name = await fetch(...); return name; } — await means 'pause here and wait for the Promise.' async functions always return Promises.
- try/catch/finally — Error Handling — try { await mayFail(); } catch(e) { console.error(e); } finally { cleanup(); } — same as other languages. finally ALWAYS runs.
- Promise.all — Parallel Execution — await Promise.all([promise1, promise2, promise3]) runs ALL at once. If any fails, all fail. 10x faster than running them one-by-one!
- Custom Error Classes — throw new Error('message') or throw new CustomError(). Catch with catch(error). Better error handling than undefined/null.
- .then/.catch — Old Way (Know It!) — promise.then(value => {}).catch(error => {}) — works but gets messy with chains. Use async/await instead! But you'll see this in old code.
- Promise.race — await Promise.race([promise1, promise2]) — whichever promise finishes FIRST wins, and the rest are ignored. This is perfect for implementing timeouts: race your API call against a timer, and if the timer wins, you know the request took too long!
- Throw vs Return in async — throw new Error() in an async function makes the Promise reject. Return a value makes it fulfill. Choose wisely!
Code example
// Promise states
const promise = fetch("https://api.example.com/user");
// PENDING: waiting for response
// then either:
// FULFILLED: response arrived
// REJECTED: network error
// async/await — the clean way! ✨
async function getUser() {
try {
const response = await fetch("https://api.example.com/user");
const user = await response.json();
console.log(user.name);
return user;
} catch (error) {
console.error("Failed:", error);
// handle error
} finally {
console.log("Request done!"); // always runs
}
}
// Call async function
const user = await getUser();
// Multiple async operations
async function loadDashboard() {
const user = await getUser(1); // wait for user
const posts = await getPosts(user.id); // THEN wait for posts
// Total time: 2sec + 2sec = 4 seconds ⏳
return { user, posts };
}
// Promise.all — parallel execution! 🚀
async function loadDashboardFast() {
const [user, posts, comments] = await Promise.all([
getUser(1),
getPosts(1),
getComments(1)
]);
// All run at SAME TIME!
// Total time: 2 seconds (instead of 6!) ⚡
return { user, posts, comments };
}
// .then/.catch (old way — avoid!)
fetch("https://api.example.com/user")
.then(res => res.json())
.then(user => console.log(user.name))
.catch(error => console.error(error));
// Gets messy with more steps!
// Throwing errors in async
async function doSomething() {
if (!user) {
throw new Error("User not found!"); // Promise rejects
}
return "Success!"; // Promise fulfills
}
// Promise.race — first to finish wins
const result = await Promise.race([
fetchFromServer1(),
fetchFromServer2(),
timeoutAfter(5000)
]);
// whichever completes first is returned!Line-by-line walkthrough
- 1. Promise states
- 2. Declaring a variable
- 3. PENDING: waiting for response
- 4. then either:
- 5. FULFILLED: response arrived
- 6. REJECTED: network error
- 7.
- 8. async/await — the clean way! ✨
- 9. Declaring a function
- 10. Opening try block for error handling
- 11. Declaring a variable
- 12. Declaring a variable
- 13. Printing output to the console
- 14. Returning a value
- 15. Catching any errors from the try block
- 16.
- 17. handle error
- 18. Finally block - runs regardless of success or failure
- 19. Printing output to the console
- 20. Closing block
- 21. Closing block
- 22.
- 23. Call async function
- 24. Declaring a variable
- 25.
- 26. Multiple async operations
- 27. Declaring a function
- 28. Declaring a variable
- 29. Declaring a variable
- 30. Total time: 2sec + 2sec = 4 seconds ⏳
- 31. Returning a value
- 32. Closing block
- 33.
- 34. Promise.all — parallel execution! 🚀
- 35. Declaring a function
- 36. Declaring a variable
- 37.
- 38.
- 39.
- 40.
- 41. All run at SAME TIME!
- 42. Total time: 2 seconds (instead of 6!) ⚡
- 43. Returning a value
- 44. Closing block
- 45.
- 46. .then/.catch (old way — avoid!)
- 47.
- 48. Method chaining on the previous expression
- 49. Method chaining on the previous expression
- 50. Method chaining on the previous expression
- 51. Gets messy with more steps!
- 52.
- 53. Throwing errors in async
- 54. Declaring a function
- 55. Conditional check
- 56. Throwing an error
- 57. Closing block
- 58. Returning a value
- 59. Closing block
- 60.
- 61. Promise.race — first to finish wins
- 62. Declaring a variable
- 63.
- 64.
- 65.
- 66.
- 67. whichever completes first is returned!
Spot the bug
function getUser() {
const user = await fetch("/api/user");
return user.json();
}Need a hint?
What keyword is missing from the function declaration?
Show answer
You can only use 'await' inside an 'async' function, but this function is not declared as async. Fix: change 'function getUser()' to 'async function getUser()'.
Explain like I'm 5
Imagine ordering a toy online. You don't get it right away - you get a tracking number (Promise). You can wait by the door all day (old way) or go play and someone tells you when it arrives (async/await). Much better!
Fun fact
Promises were inspired by other languages' Futures (Dart) and Completable Futures (Java). async/await was added to make Promises feel like normal synchronous code!
Hands-on challenge
Create async functions that each take 1 second. Run 5 of them sequentially (measures time), then in parallel with Promise.all. How much faster?
More resources
- MDN Async/Await (MDN Web Docs)
- JavaScript Promises in 100 Seconds (Fireship)
- MDN Using Promises (MDN Web Docs)