Async JavaScript
Async JavaScript cheat sheet covering Promises, async/await, error handling, Promise.all, race conditions, and concurrency patterns.
Other JavaScript Sheets
Sign in to mark items as known and track your progress.
Sign inCallbacks
Basic Callbacks
Traditional pattern for handling asynchronous operations with callback functions
// Simple callback
function getData(callback) {
setTimeout(() => {
callback('Data loaded');
}, 1000);
}
getData(result => console.log(result));
// Error-first callback (Node.js pattern)
function readFile(path, callback) {
fs.readFile(path, (err, data) => {
if (err) callback(err, null);
else callback(null, data);
});
}Callback Hell
The problem of deeply nested callbacks and how to avoid it with modern patterns
// Callback hell (pyramid of doom)
getData((a) => {
getMoreData(a, (b) => {
getMoreData(b, (c) => {
getMoreData(c, (d) => {
console.log(d);
});
});
});
});
// Better: Named functions
function step1(callback) { getData(callback); }
function step2(a, callback) { getMoreData(a, callback); }
function step3(b, callback) { getMoreData(b, callback); }Timers & Event Loop
Timer Functions
Schedule code execution after delays or at regular intervals using built-in timer functions
// setTimeout
const timeoutId = setTimeout(() => {
console.log('Executed after 2 seconds');
}, 2000);
// setInterval
const intervalId = setInterval(() => {
console.log('Every second');
}, 1000);
// Clear timers
clearTimeout(timeoutId);
clearInterval(intervalId);
// setImmediate (Node.js)
setImmediate(() => {
console.log('Execute after I/O');
});Event Loop
Understand how JavaScript handles asynchronous operations through the event loop mechanism
// Event loop phases
console.log('1'); // Call stack
setTimeout(() => console.log('2'), 0); // Macrotask queue
Promise.resolve()
.then(() => console.log('3')); // Microtask queue
console.log('4'); // Call stack
// Output: 1, 4, 3, 2Promises
Creating Promises
Create a new Promise that resolves or rejects based on asynchronous operations
// Create a promise
const promise = new Promise((resolve, reject) => {
if (success) resolve(value);
else reject(error);
});
// Using promises
promise
.then(result => console.log(result))
.catch(error => console.error(error))
.finally(() => console.log('Done'));
// Promise from callback
function promisify(fn) {
return (...args) => new Promise((resolve, reject) => {
fn(...args, (err, data) => {
if (err) reject(err);
else resolve(data);
});
});
}Promise Combinators
Utility methods for coordinating multiple promises with different execution strategies
// Promise.all - all must succeed
Promise.all([p1, p2, p3])
.then(results => console.log(results));
// Promise.race - first to settle wins
Promise.race([p1, p2, p3])
.then(winner => console.log(winner));
// Promise.allSettled - wait for all
Promise.allSettled([p1, p2, p3])
.then(results => results.forEach(r =>
console.log(r.status, r.value || r.reason)
));
// Promise.any - first success wins
Promise.any([p1, p2, p3])
.then(first => console.log(first));Async/Await
Basic Async/Await
Modern syntax for writing asynchronous code that looks and behaves like synchronous code
// Async function
async function getData() {
const response = await fetch('/api/data');
const data = await response.json();
return data;
}
// Error handling
async function safeGet() {
try {
const data = await getData();
console.log(data);
} catch (error) {
console.error('Failed:', error);
}
}
// Parallel execution
async function parallel() {
const [a, b] = await Promise.all([
fetch('/api/a'),
fetch('/api/b')
]);
}Advanced Async Patterns
Complex patterns for handling concurrency, throttling, and async flow control
// Async iterator
async function* asyncGenerator() {
yield await fetch('/page/1');
yield await fetch('/page/2');
yield await fetch('/page/3');
}
for await (const page of asyncGenerator()) {
console.log(await page.json());
}
// Retry pattern
async function retry(fn, retries = 3) {
try {
return await fn();
} catch (err) {
if (retries > 0) {
await delay(1000);
return retry(fn, retries - 1);
}
throw err;
}
}await using & Promise.withResolvers (ES2024)
Explicit resource management with `using` + the deferred-promise helper
// await using — auto-dispose async resources when the block ends
class DbConnection {
async [Symbol.asyncDispose]() { await this.close() }
}
async function getUser(id) {
await using conn = await pool.acquire() // released no matter how we exit
return conn.query('SELECT * FROM users WHERE id = $1', [id])
}
// Promise.withResolvers — get a promise + its resolve/reject up front
function once(emitter, event) {
const { promise, resolve, reject } = Promise.withResolvers()
emitter.once(event, resolve)
emitter.once('error', reject)
return promise
}Abort & Error Handling
AbortController
Cancel ongoing asynchronous operations like fetch requests or long-running tasks
// Basic abort
const controller = new AbortController();
const signal = controller.signal;
fetch('/api/data', { signal })
.catch(err => {
if (err.name === 'AbortError') {
console.log('Aborted');
}
});
// Abort after timeout
setTimeout(() => controller.abort(), 5000);
// Multiple operations
const controller = new AbortController();
Promise.all([
fetch('/api/1', { signal: controller.signal }),
fetch('/api/2', { signal: controller.signal })
]);
controller.abort(); // Cancels allError Handling Strategies
Best practices and patterns for handling errors in asynchronous JavaScript code
// Try/catch with async/await
async function safe() {
try {
return await riskyOperation();
} catch (error) {
logger.error(error);
return defaultValue;
}
}
// Promise error handling
promise
.then(handleSuccess)
.catch(handleError)
.finally(cleanup);
// Global error handlers
window.addEventListener('unhandledrejection', e => {
console.error('Unhandled promise:', e.reason);
});