Async / Await
Write async code that looks and feels synchronous. The cleanest pattern.
async Functions
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().
The await Keyword
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.
Sequential vs Parallel
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 };
}Error Handling
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!
Common Patterns
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();FAQ
Common questions about async/await.