Promises, async/await, callbacks, and error handling
Simple callback function
function fetchData(callback) {
setTimeout(() => {
callback("Data received");
}, 1000);
}
Callback with error handling
function fetchData(success, error) {
if (Math.random() > 0.5) {
success("Data");
} else {
error("Failed");
}
}
Nested callbacks (avoid)
fetchData1((data1) => {
fetchData2(data1, (data2) => {
fetchData3(data2, (data3) => {
console.log(data3);
});
});
});
Create a new promise
const promise = new Promise((resolve, reject) => {
// async operation
resolve("Success");
});
Pending, Fulfilled, Rejected
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Done"); // Fulfilled
// reject("Error"); // Rejected
}, 1000);
});
Handle promise resolution
promise.then(
(result) => console.log(result),
(error) => console.error(error)
);
Handle promise rejection
promise.catch((error) => {
console.error("Error:", error);
});
Execute after promise settles
promise.finally(() => {
console.log("Promise settled");
});
Wait for all promises
Promise.all([promise1, promise2, promise3])
.then((results) => console.log(results));
First promise to settle
Promise.race([promise1, promise2])
.then((result) => console.log(result));
Wait for all to settle
Promise.allSettled([promise1, promise2])
.then((results) => console.log(results));
Declare async function
async function fetchData() {
return "Data";
}
Wait for promise to resolve
async function getData() {
const result = await fetch("/api/data");
return result.json();
}
Try-catch with async/await
async function getData() {
try {
const result = await fetch("/api/data");
return result.json();
} catch (error) {
console.error("Error:", error);
}
}
Sequential async operations
async function processData() {
const user = await fetchUser();
const posts = await fetchPosts(user.id);
const comments = await fetchComments(posts[0].id);
return { user, posts, comments };
}
Concurrent async operations
async function processData() {
const [user, posts, comments] = await Promise.all([
fetchUser(),
fetchPosts(),
fetchComments()
]);
return { user, posts, comments };
}
Simple GET request
fetch("/api/data")
.then(response => response.json())
.then(data => console.log(data));
Send data with POST
fetch("/api/data", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "John" })
});
Handle fetch errors
fetch("/api/data")
.then(response => {
if (!response.ok) throw new Error("HTTP error");
return response.json();
})
.catch(error => console.error(error));
Modern fetch usage
async function fetchData() {
try {
const response = await fetch("/api/data");
if (!response.ok) throw new Error("HTTP error");
return await response.json();
} catch (error) {
console.error("Error:", error);
}
}
Create a promise that resolves after a delay
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Usage
async function example() {
console.log("Start");
await delay(2000);
console.log("After 2 seconds");
}
// Or with value
function delayWithValue(ms, value) {
return new Promise(resolve => setTimeout(() => resolve(value), ms));
}
Retry failed operations with exponential backoff
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;
console.log(`Attempt ${attempt} failed, retrying...`);
await new Promise(resolve =>
setTimeout(resolve, delay * Math.pow(2, attempt - 1))
);
}
}
}
// Usage
const result = await retry(() => fetch("/api/data"));
Process requests in batches to avoid overwhelming the server
async function processBatch(items, batchSize = 3) {
const results = [];
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);
const batchPromises = batch.map(item => processItem(item));
const batchResults = await Promise.all(batchPromises);
results.push(...batchResults);
// Optional delay between batches
if (i + batchSize < items.length) {
await new Promise(resolve => setTimeout(resolve, 100));
}
}
return results;
}
async function processItem(item) {
// Simulate async processing
await new Promise(resolve => setTimeout(resolve, 100));
return `Processed: ${item}`;
}
Process tasks sequentially with a queue
class AsyncQueue {
constructor() {
this.queue = [];
this.processing = false;
}
async add(task) {
return new Promise((resolve, reject) => {
this.queue.push({ task, resolve, reject });
this.process();
});
}
async process() {
if (this.processing || this.queue.length === 0) return;
this.processing = true;
while (this.queue.length > 0) {
const { task, resolve, reject } = this.queue.shift();
try {
const result = await task();
resolve(result);
} catch (error) {
reject(error);
}
}
this.processing = false;
}
}
// Usage
const queue = new AsyncQueue();
queue.add(() => fetch("/api/data1"));
queue.add(() => fetch("/api/data2"));
Cancel ongoing promises using AbortController
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 response.json();
} catch (error) {
clearTimeout(timeoutId);
if (error.name === 'AbortError') {
throw new Error('Request timed out');
}
throw error;
}
}
// Usage
try {
const data = await fetchWithTimeout('/api/data', 3000);
console.log(data);
} catch (error) {
console.error('Error:', error.message);
}
Handle async operations in event listeners
// Button click with loading state
const button = document.getElementById('submit');
button.addEventListener('click', async (e) => {
e.preventDefault();
// Disable button and show loading
button.disabled = true;
button.textContent = 'Loading...';
try {
const formData = new FormData(e.target.form);
const response = await fetch('/api/submit', {
method: 'POST',
body: formData
});
if (!response.ok) throw new Error('Submission failed');
const result = await response.json();
showSuccess(result.message);
} catch (error) {
showError(error.message);
} finally {
// Re-enable button
button.disabled = false;
button.textContent = 'Submit';
}
});
Always handle errors in async operations with try-catch or .catch()
Use Promise.all()
for parallel operations, not sequential awaits
Avoid callback hell by using promises or async/await
Use AbortController
for cancelling fetch requests
Implement proper loading states and error boundaries in UI