Async JavaScript
Async JavaScript cheat sheet covering Promises, async/await, error handling, Promise.all, race conditions, and concurrency patterns.
Other JavaScript Sheets
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);
});
}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
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');
});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
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);
});
});
}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
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')
]);
}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;
}
}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
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 allBest 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);
});