Loading...
Loading...
Write async code that looks and feels synchronous. The cleanest pattern.
Browsers and servers spend most of their time waiting — for network responses, timers, and user input. Async JavaScript lets your program stay responsive while work happens in the background using callbacks, Promises, and async/await.
async/await lets you write asynchronous code that reads top-to-bottom — pause inside an async function until a Promise settles, without blocking the main thread.
Synchronous code runs one line at a time. But real applications wait for network requests, file reads, and timers. Async JavaScript lets your program keep running while that work happens in the background — without freezing the UI.
This module covers the full async story: callbacks, Promises, async/await, and the Fetch API. Visual timelines show when tasks enter the queue, when they resolve, and why async/await is syntactic sugar over Promises — not magic.
async/await is how modern apps load data, authenticate users, and save forms. Misusing await inside loops, skipping try/catch, or mixing raw promises and await creates race conditions that only show up in production.
Mark a function as async — it always returns a Promise.
// Regular function: function greet() { return "Hello"; } greet(); // "Hello" // Async function: async function greetAsync() { return "Hello"; } greetAsync(); // Promise { "Hello" }
An async function ALWAYS returns a Promise. Even if you return a plain value, it's automatically wrapped in Promise.resolve().
Pause execution until a Promise resolves.
async function loadUser() { console.log("Start"); const response = await fetch("/api/user"); // ⏸️ Pauses here until fetch resolves const user = await response.json(); // ⏸️ Pauses here until .json() resolves console.log("Got user:", user.name); return user; }
await pauses the async function (not the whole program!) until the Promise settles. It then unwraps the resolved value.
💡 await doesn't block the main thread. Other code (event handlers, other async functions) keeps running while this function is paused.
Don't accidentally make parallel operations sequential.
// ❌ SEQUENTIAL (slow) — each waits for the previous: async function loadAll() { const users = await fetchUsers(); // 1s const posts = await fetchPosts(); // 1s const comments = await fetchComments(); // 1s // Total: ~3 seconds 😬 } // ✅ PARALLEL (fast) — all start at once: async function loadAll() { const [users, posts, comments] = await Promise.all([ fetchUsers(), // 1s ─┐ fetchPosts(), // 1s ─┤ all run simultaneously fetchComments() // 1s ─┘ ]); // Total: ~1 second 🚀 }
Sequential
Use when each step depends on the previous result
Parallel
Use when operations are independent of each other
// Mixed: some sequential, some parallel async function loadDashboard(userId) { // Sequential (posts need user first): const user = await fetchUser(userId); // Parallel (these are independent): const [posts, notifications] = await Promise.all([ fetchPosts(user.id), fetchNotifications(user.id) ]); return { user, posts, notifications }; }
try/catch works perfectly with await.
async function loadData() { try { const res = await fetch("/api/data"); if (!res.ok) { throw new Error(`HTTP error: ${res.status}`); } const data = await res.json(); return data; } catch (error) { // Catches: network errors, HTTP errors, JSON parse errors console.error("Failed to load:", error.message); return null; // fallback value } finally { // Always runs — perfect for cleanup: hideLoadingSpinner(); } }
⚠️ If you don't use try/catch, rejected promises in async functions become unhandled rejections. Always handle errors!
Useful async/await patterns you'll use often.
// Pattern 1: Retry with delay async function fetchWithRetry(url, retries = 3) { for (let i = 0; i < retries; i++) { try { const res = await fetch(url); if (res.ok) return await res.json(); } catch (err) { if (i === retries - 1) throw err; await new Promise(r => setTimeout(r, 1000 * (i + 1))); } } }
// Pattern 2: Process array items sequentially async function processItems(items) { const results = []; for (const item of items) { const result = await processItem(item); results.push(result); } return results; } // Pattern 3: Process array items in parallel async function processItemsParallel(items) { return Promise.all(items.map(item => processItem(item))); }
// Pattern 4: Async IIFE (immediately invoked) (async () => { const data = await fetchData(); console.log(data); })(); // Pattern 5: Top-level await (ES modules) // In .mjs files or <script type="module">: const config = await loadConfig();
Common questions about async/await.