Async / Await
async/await with Fetch
The async/await syntax (ES2017) makes asynchronous code look and behave like synchronous code. An async function always returns a Promise. Inside it, await pauses execution until the awaited Promise resolves — without blocking the main thread.
// async function — always returns a Promise
async function getUser(id) {
// try-catch replaces .catch() for error handling
try {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const user = await response.json(); // await the body parsing too
return user;
} catch (error) {
console.error('getUser failed:', error.message);
throw error; // re-throw so callers can handle it
} finally {
// finally always runs — good for cleanup
console.log('getUser request complete');
}
}
// Call the async function
getUser(1)
.then(user => console.log('User:', user.name))
.catch(err => console.error('Caught:', err.message));
// ---- SEQUENTIAL — each request waits for the previous ----
// Total time = sum of all request times (slow!)
async function loadSequential() {
const user = await fetch('/api/users/1').then(r => r.json());
const posts = await fetch('/api/posts?userId=1').then(r => r.json());
const profile = await fetch('/api/profile/1').then(r => r.json());
// Runs in series: user → posts → profile
return { user, posts, profile };
}
// ---- PARALLEL with Promise.all — all requests fire simultaneously ----
// Total time = longest single request (fast!)
async function loadParallel() {
const [user, posts, profile] = await Promise.all([
fetch('/api/users/1').then(r => r.json()),
fetch('/api/posts?userId=1').then(r => r.json()),
fetch('/api/profile/1').then(r => r.json())
]);
// All three requests run concurrently
return { user, posts, profile };
}
// ---- Promise.allSettled — get results even if some fail ----
async function loadWithFallback() {
const results = await Promise.allSettled([
fetch('/api/users/1').then(r => r.json()),
fetch('/api/posts?userId=1').then(r => r.json()),
fetch('/api/missing-endpoint').then(r => r.json()) // this will fail
]);
results.forEach((result, i) => {
if (result.status === 'fulfilled') {
console.log(`Request ${i} succeeded:`, result.value);
} else {
console.warn(`Request ${i} failed:`, result.reason.message);
}
});
}
// Async IIFE — run async code at the top level (pre-ES2022)
(async () => {
try {
const res = await fetch('/api/config');
const config = await res.json();
console.log('App config loaded:', config);
initApp(config);
} catch (err) {
console.error('Failed to load config:', err);
}
})();
// Top-level await (ES2022, requires type="module")
// <script type="module">
// const res = await fetch('/api/config');
// const config = await res.json();
// initApp(config);
// </script>
// Error propagation through async call chains
async function step1() {
const data = await fetch('/api/step1').then(r => r.json());
return data.value;
}
async function step2(value) {
const data = await fetch(`/api/step2?v=${value}`).then(r => r.json());
return data.result;
}
async function runPipeline() {
try {
const v1 = await step1();
const v2 = await step2(v1);
console.log('Pipeline result:', v2);
} catch (err) {
// Catches errors from step1 OR step2
console.error('Pipeline failed:', err.message);
}
}
// Pattern 1: Loading state management
async function loadAndRender(url, containerId) {
const container = document.getElementById(containerId);
container.innerHTML = '<div class="spinner">Loading...</div>';
try {
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
container.innerHTML = renderData(data);
} catch (err) {
container.innerHTML = `<p class="error">${err.message}</p>`;
}
}
// Pattern 2: Polling — check for updates every N seconds
async function pollForStatus(jobId, intervalMs = 2000) {
while (true) {
const res = await fetch(`/api/jobs/${jobId}`);
const job = await res.json();
if (job.status === 'completed') {
console.log('Job done:', job.result);
break;
} else if (job.status === 'failed') {
throw new Error('Job failed: ' + job.error);
}
// Wait before next poll
await new Promise(resolve => setTimeout(resolve, intervalMs));
}
}
// Pattern 3: Race — use whichever request finishes first
async function fetchWithFallback(primaryUrl, fallbackUrl) {
try {
return await Promise.race([
fetch(primaryUrl).then(r => r.json()),
new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 3000))
]);
} catch {
console.warn('Primary failed, using fallback');
return fetch(fallbackUrl).then(r => r.json());
}
}
Ready to Level Up Your Skills?
Explore 500+ free tutorials across 20+ languages and frameworks.