Introduction
Asynchronous programming is fundamental to JavaScript. Understanding modern async patterns helps you write cleaner, more efficient code.
This guide covers async JavaScript in 2026.
Promises
Basic Promise
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Data loaded');
}, 1000);
});
promise.then((data) => console.log(data));
Promise Methods
// Promise.all - all must succeed
Promise.all([
fetch('/api/users'),
fetch('/api/posts'),
])
.then(([users, posts]) => console.log(users, posts));
// Promise.race - first to complete
Promise.race([
fetch('/api/fast'),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), 5000)
])
]);
// Promise.allSettled - all complete
Promise.allSettled([
fetch('/api/users'),
fetch('/api/posts'),
]).then((results) => {
results.forEach((result) => {
if (result.status === 'fulfilled') {
console.log(result.value);
} else {
console.error(result.reason);
}
});
});
// Promise.any - first success
Promise.any([
fetch('/api/primary'),
fetch('/api/backup'),
]).then((data) => console.log(data));
Async/Await
Basic Usage
async function fetchData() {
try {
const response = await fetch('/api/data');
const data = await response.json();
return data;
} catch (error) {
console.error('Error:', error);
throw error;
}
}
Parallel Execution
// Sequential - slower
const user = await fetchUser(id);
const posts = await fetchPosts(id);
// Parallel - faster
const [user, posts] = await Promise.all([
fetchUser(id),
fetchPosts(id),
]);
Error Handling
// Try-catch
async function getData() {
try {
const data = await fetchData();
return data;
} catch (error) {
console.error(error);
return null;
}
}
// Error boundaries
async function getData() {
const data = await fetchData().catch((err) => {
console.error(err);
return defaultData;
});
return data;
}
Async Patterns
Retry Pattern
async function retry(fn, maxAttempts = 3, delay = 1000) {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
} catch (error) {
if (attempt === maxAttempts) throw error;
await new Promise((r) => setTimeout(r, delay * attempt));
}
}
}
// Usage
const data = await retry(() => fetch('/api/data'));
Timeout Pattern
function withTimeout(promise, ms) {
return Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), ms)
),
]);
}
// Usage
try {
const data = await withTimeout(fetch('/api/data'), 5000);
} catch (error) {
console.error('Request timed out');
}
Queue Pattern
class AsyncQueue {
constructor(concurrency = 2) {
this.concurrency = concurrency;
this.running = 0;
this.queue = [];
}
async add(fn) {
return new Promise((resolve, reject) => {
this.queue.push({ fn, resolve, reject });
this.process();
});
}
async process() {
while (this.running < this.concurrency && this.queue.length) {
const { fn, resolve, reject } = this.queue.shift();
this.running++;
try {
const result = await fn();
resolve(result);
} catch (error) {
reject(error);
} finally {
this.running--;
this.process();
}
}
}
}
// Usage
const queue = new AsyncQueue(3);
const results = await Promise.all(
items.map(item => queue.add(() => processItem(item)))
Debounce & Throttle
// Debounce - wait for pause
function debounce(fn, delay) {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn(...args), delay);
};
}
// Throttle - limit frequency
function throttle(fn, limit) {
let inThrottle;
return (...args) => {
if (!inThrottle) {
fn(...args);
inThrottle = true;
setTimeout(() => (inThrottle = false), limit);
}
};
}
// Usage
const debouncedSearch = debounce((query) => search(query), 300);
const throttledScroll = throttle(() => handleScroll(), 100);
Modern Async Features
For Await Of
async function* fetchPages() {
let page = 1;
while (true) {
const data = await fetch(`/api/items?page=${page}`);
const items = await data.json();
if (items.length === 0) break;
yield items;
page++;
}
}
// Usage
for await (const items of fetchPages()) {
console.log(items);
}
Promise.withResolvers
// Modern alternative to new Promise
const { promise, resolve, reject } = Promise.withResolvers();
fetch('/api/data')
.then(resolve)
.catch(reject);
const data = await promise;
Conclusion
Master async patterns for better JavaScript. Use Promise.all for parallel operations, implement retry for reliability, and handle errors gracefully.
Comments