JavaScript Async/Await
Master async/await syntax, the modern way to work with promises that makes asynchronous code look and behave more like synchronous code
50 min•By Priygop Team•Last updated: Feb 2026
Async/Await Basics
Async/await is built on top of promises and provides a more intuitive way to write asynchronous code. It allows you to write promise-based code as if it were synchronous, without blocking the main thread.
Async Function Examples
Example
// Basic async function
async function fetchUserData(userId) {
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const userData = await response.json();
return userData;
} catch (error) {
console.error('Error fetching user:', error.message);
throw error;
}
}
// Async function with multiple awaits
async function processUser(userId) {
try {
// Fetch user data
const user = await fetchUserData(userId);
console.log('User fetched:', user);
// Fetch user posts
const posts = await fetchUserPosts(userId);
console.log('Posts fetched:', posts);
// Fetch user comments
const comments = await fetchUserComments(userId);
console.log('Comments fetched:', comments);
return {
user,
posts,
comments
};
} catch (error) {
console.error('Error processing user:', error.message);
throw error;
}
}
// Async function with parallel execution
async function processUserParallel(userId) {
try {
// Execute all promises in parallel
const [user, posts, comments] = await Promise.all([
fetchUserData(userId),
fetchUserPosts(userId),
fetchUserComments(userId)
]);
return { user, posts, comments };
} catch (error) {
console.error('Error in parallel processing:', error.message);
throw error;
}
}
// Async function with conditional logic
async function fetchDataWithFallback(primaryUrl, fallbackUrl) {
try {
const response = await fetch(primaryUrl);
if (response.ok) {
return await response.json();
}
} catch (error) {
console.log('Primary URL failed, trying fallback...');
}
try {
const response = await fetch(fallbackUrl);
if (response.ok) {
return await response.json();
}
} catch (error) {
throw new Error('Both URLs failed');
}
}
// Async function with timeout
async function fetchWithTimeout(url, timeout = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, {
signal: controller.signal
});
clearTimeout(timeoutId);
return await response.json();
} catch (error) {
clearTimeout(timeoutId);
if (error.name === 'AbortError') {
throw new Error('Request timeout');
}
throw error;
}
}
// Async function with retry logic
async function fetchWithRetry(url, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(url);
if (response.ok) {
return await response.json();
}
} catch (error) {
if (attempt === maxRetries) {
throw error;
}
console.log(`Attempt ${attempt} failed, retrying...`);
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
}
}
}
// Async function in loops
async function processItems(items) {
const results = [];
// Sequential processing
for (const item of items) {
const result = await processItem(item);
results.push(result);
}
return results;
}
async function processItemsParallel(items) {
// Parallel processing
const promises = items.map(item => processItem(item));
return await Promise.all(promises);
}
// Async function with error boundaries
async function safeAsyncOperation(operation) {
try {
return await operation();
} catch (error) {
console.error('Operation failed:', error.message);
return null;
}
}Practice Exercise: Async/Await
Example
// Exercise: Build an Async Data Processor
class AsyncDataProcessor {
constructor() {
this.cache = new Map();
this.processing = new Set();
}
async processData(data, processor) {
const cacheKey = JSON.stringify(data);
if (this.cache.has(cacheKey)) {
console.log('Returning cached result');
return this.cache.get(cacheKey);
}
if (this.processing.has(cacheKey)) {
console.log('Waiting for ongoing processing...');
while (this.processing.has(cacheKey)) {
await new Promise(resolve => setTimeout(resolve, 100));
}
return this.cache.get(cacheKey);
}
this.processing.add(cacheKey);
try {
const result = await processor(data);
this.cache.set(cacheKey, result);
return result;
} finally {
this.processing.delete(cacheKey);
}
}
async batchProcess(items, processor, batchSize = 5) {
const results = [];
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);
const batchPromises = batch.map(item => processor(item));
const batchResults = await Promise.all(batchPromises);
results.push(...batchResults);
console.log(`Processed batch ${Math.floor(i / batchSize) + 1}`);
}
return results;
}
async processWithProgress(items, processor, onProgress) {
const results = [];
let completed = 0;
const promises = items.map(async (item, index) => {
const result = await processor(item);
completed++;
onProgress(completed, items.length, result);
return result;
});
return await Promise.all(promises);
}
}
// Exercise: Build an Async Cache Manager
class AsyncCacheManager {
constructor() {
this.cache = new Map();
this.expiryTimes = new Map();
}
async get(key) {
if (this.isExpired(key)) {
this.delete(key);
return null;
}
return this.cache.get(key);
}
set(key, value, ttl = 60000) { // Default 1 minute TTL
this.cache.set(key, value);
this.expiryTimes.set(key, Date.now() + ttl);
}
delete(key) {
this.cache.delete(key);
this.expiryTimes.delete(key);
}
isExpired(key) {
const expiryTime = this.expiryTimes.get(key);
return expiryTime && Date.now() > expiryTime;
}
async getOrSet(key, fetcher, ttl = 60000) {
let value = await this.get(key);
if (value === null) {
value = await fetcher();
this.set(key, value, ttl);
}
return value;
}
clear() {
this.cache.clear();
this.expiryTimes.clear();
}
getStats() {
return {
size: this.cache.size,
keys: Array.from(this.cache.keys())
};
}
}
// Exercise: Build an Async Task Scheduler
class AsyncTaskScheduler {
constructor(maxConcurrent = 3) {
this.maxConcurrent = maxConcurrent;
this.running = 0;
this.queue = [];
}
async schedule(task, priority = 0) {
return new Promise((resolve, reject) => {
this.queue.push({
task,
priority,
resolve,
reject
});
this.queue.sort((a, b) => b.priority - a.priority);
this.process();
});
}
async process() {
if (this.running >= this.maxConcurrent || this.queue.length === 0) {
return;
}
const { task, resolve, reject } = this.queue.shift();
this.running++;
try {
const result = await task();
resolve(result);
} catch (error) {
reject(error);
} finally {
this.running--;
this.process(); // Process next task
}
}
getStats() {
return {
running: this.running,
queued: this.queue.length,
maxConcurrent: this.maxConcurrent
};
}
}
// Test the exercises
const processor = new AsyncDataProcessor();
const cache = new AsyncCacheManager();
const scheduler = new AsyncTaskScheduler(2);
// Test data processor
const data = [1, 2, 3, 4, 5];
const processorFn = async (item) => {
await new Promise(resolve => setTimeout(resolve, 1000));
return item * 2;
};
processor.processData(data, processorFn)
.then(result => console.log('Processed result:', result));
// Test cache manager
cache.getOrSet('user:123', async () => {
await new Promise(resolve => setTimeout(resolve, 1000));
return { id: 123, name: 'John' };
})
.then(user => console.log('Cached user:', user));
// Test task scheduler
const tasks = [
() => new Promise(resolve => setTimeout(() => resolve('Task 1'), 2000)),
() => new Promise(resolve => setTimeout(() => resolve('Task 2'), 1000)),
() => new Promise(resolve => setTimeout(() => resolve('Task 3'), 1500)),
() => new Promise(resolve => setTimeout(() => resolve('Task 4'), 500))
];
tasks.forEach((task, index) => {
scheduler.schedule(task, index)
.then(result => console.log(`Scheduled task ${index + 1}: ${result}`));
});