javascript
examples
examples.js⚡javascript
/**
* Async Patterns - Examples
* Common patterns for managing asynchronous operations
*/
// Utility function for simulating async operations
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function asyncTask(id, duration = 100) {
console.log(`Task ${id}: started`);
await delay(duration);
console.log(`Task ${id}: completed`);
return `Result ${id}`;
}
// =============================================================================
// 1. SEQUENTIAL EXECUTION
// =============================================================================
console.log('--- Sequential Execution ---');
async function processSequential(items) {
const results = [];
console.time('Sequential');
for (const item of items) {
const result = await asyncTask(item, 100);
results.push(result);
}
console.timeEnd('Sequential');
return results;
}
// Note: Takes items.length * 100ms
// processSequential([1, 2, 3, 4, 5]);
// =============================================================================
// 2. PARALLEL EXECUTION
// =============================================================================
console.log('\n--- Parallel Execution ---');
async function processParallel(items) {
console.time('Parallel');
const results = await Promise.all(items.map((item) => asyncTask(item, 100)));
console.timeEnd('Parallel');
return results;
}
// Note: Takes max(100, 100, ...) = 100ms
// processParallel([1, 2, 3, 4, 5]);
// =============================================================================
// 3. BATCHED EXECUTION
// =============================================================================
console.log('\n--- Batched Execution ---');
async function processBatched(items, batchSize = 2) {
const results = [];
console.time('Batched');
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);
console.log(`Processing batch: ${batch.join(', ')}`);
const batchResults = await Promise.all(
batch.map((item) => asyncTask(item, 100))
);
results.push(...batchResults);
}
console.timeEnd('Batched');
return results;
}
// Note: Takes ceiling(items.length / batchSize) * 100ms
// processBatched([1, 2, 3, 4, 5], 2);
// =============================================================================
// 4. CONCURRENT LIMIT
// =============================================================================
console.log('\n--- Concurrent Limit ---');
async function withConcurrencyLimit(tasks, limit) {
const results = [];
const executing = [];
for (const task of tasks) {
const promise = Promise.resolve().then(task);
results.push(promise);
if (limit <= tasks.length) {
const executing = promise.then(() =>
executing.splice(executing.indexOf(executing), 1)
);
executing.push(executing);
if (executing.length >= limit) {
await Promise.race(executing);
}
}
}
return Promise.all(results);
}
// Better implementation with a pool
class ConcurrencyPool {
constructor(limit) {
this.limit = limit;
this.running = 0;
this.queue = [];
}
async run(task) {
return new Promise((resolve, reject) => {
this.queue.push(async () => {
try {
resolve(await task());
} catch (error) {
reject(error);
}
});
this.tryRun();
});
}
async tryRun() {
while (this.running < this.limit && this.queue.length > 0) {
const task = this.queue.shift();
this.running++;
try {
await task();
} finally {
this.running--;
this.tryRun();
}
}
}
}
async function demonstrateConcurrencyPool() {
console.log('\n--- Concurrency Pool Demo ---');
const pool = new ConcurrencyPool(2);
const tasks = [1, 2, 3, 4, 5].map((id) => pool.run(() => asyncTask(id, 100)));
console.time('Pool');
const results = await Promise.all(tasks);
console.timeEnd('Pool');
console.log('Results:', results);
}
// =============================================================================
// 5. PIPELINE PATTERN
// =============================================================================
console.log('\n--- Pipeline Pattern ---');
// Simple pipeline
async function pipeline(value, ...fns) {
let result = value;
for (const fn of fns) {
result = await fn(result);
}
return result;
}
// Pipe function (creates reusable pipeline)
function pipe(...fns) {
return async (value) => {
let result = value;
for (const fn of fns) {
result = await fn(result);
}
return result;
};
}
// Pipeline stages
const fetchUser = async (id) => {
await delay(50);
return { id, name: `User ${id}` };
};
const enrichWithPosts = async (user) => {
await delay(50);
return { ...user, posts: ['Post 1', 'Post 2'] };
};
const formatOutput = async (user) => {
await delay(50);
return `${user.name} has ${user.posts.length} posts`;
};
// Use pipeline
async function demoPipeline() {
const result = await pipeline(1, fetchUser, enrichWithPosts, formatOutput);
console.log('Pipeline result:', result);
// Create reusable pipeline
const getUserSummary = pipe(fetchUser, enrichWithPosts, formatOutput);
console.log('Reusable pipeline:', await getUserSummary(2));
}
// =============================================================================
// 6. DEBOUNCE
// =============================================================================
console.log('\n--- Debounce ---');
function debounce(fn, wait) {
let timeout;
return function (...args) {
const later = () => {
timeout = null;
fn.apply(this, args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// Async debounce with Promise
function debounceAsync(fn, wait) {
let timeout;
let pendingPromise;
let resolver;
return function (...args) {
return new Promise((resolve, reject) => {
clearTimeout(timeout);
resolver = { resolve, reject };
timeout = setTimeout(async () => {
try {
const result = await fn.apply(this, args);
resolver.resolve(result);
} catch (error) {
resolver.reject(error);
}
}, wait);
});
};
}
// Demo
const debouncedLog = debounce((msg) => console.log('Debounced:', msg), 200);
function demoDebounce() {
console.log('Firing rapid events...');
debouncedLog('first');
debouncedLog('second');
debouncedLog('third'); // Only this will fire
}
// =============================================================================
// 7. THROTTLE
// =============================================================================
console.log('\n--- Throttle ---');
function throttle(fn, limit) {
let inThrottle = false;
let lastArgs = null;
return function (...args) {
if (!inThrottle) {
fn.apply(this, args);
inThrottle = true;
setTimeout(() => {
inThrottle = false;
if (lastArgs) {
fn.apply(this, lastArgs);
lastArgs = null;
}
}, limit);
} else {
lastArgs = args;
}
};
}
// Throttle with trailing call
function throttleWithTrailing(fn, limit) {
let waiting = false;
let lastArgs = null;
const timeoutFn = () => {
if (lastArgs) {
fn.apply(this, lastArgs);
lastArgs = null;
waiting = true;
setTimeout(timeoutFn, limit);
} else {
waiting = false;
}
};
return function (...args) {
if (!waiting) {
fn.apply(this, args);
waiting = true;
setTimeout(timeoutFn, limit);
} else {
lastArgs = args;
}
};
}
const throttledLog = throttle((msg) => console.log('Throttled:', msg), 500);
// =============================================================================
// 8. RETRY PATTERN
// =============================================================================
console.log('\n--- Retry Pattern ---');
async function retry(fn, { maxAttempts = 3, delay = 100, backoff = 2 } = {}) {
let lastError;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn(attempt);
} catch (error) {
lastError = error;
console.log(`Attempt ${attempt} failed: ${error.message}`);
if (attempt < maxAttempts) {
const waitTime = delay * Math.pow(backoff, attempt - 1);
await new Promise((r) => setTimeout(r, waitTime));
}
}
}
throw lastError;
}
// With jitter
async function retryWithJitter(fn, options = {}) {
const { maxAttempts = 3, baseDelay = 100, maxDelay = 5000 } = options;
let lastError;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn(attempt);
} catch (error) {
lastError = error;
if (attempt < maxAttempts) {
// Exponential backoff with jitter
const exponentialDelay = baseDelay * Math.pow(2, attempt - 1);
const jitter = Math.random() * exponentialDelay * 0.5;
const waitTime = Math.min(exponentialDelay + jitter, maxDelay);
await new Promise((r) => setTimeout(r, waitTime));
}
}
}
throw lastError;
}
// =============================================================================
// 9. TIMEOUT PATTERN
// =============================================================================
console.log('\n--- Timeout Pattern ---');
function withTimeout(promise, ms) {
const timeout = new Promise((_, reject) => {
setTimeout(() => reject(new Error(`Timeout after ${ms}ms`)), ms);
});
return Promise.race([promise, timeout]);
}
// With cancellation
function withTimeoutAndCancel(asyncFn, ms) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), ms);
return asyncFn(controller.signal).finally(() => clearTimeout(timeout));
}
async function demoTimeout() {
const slowTask = delay(500).then(() => 'slow result');
try {
const result = await withTimeout(slowTask, 200);
console.log('Result:', result);
} catch (error) {
console.log('Timeout caught:', error.message);
}
}
// =============================================================================
// 10. POLLING PATTERN
// =============================================================================
console.log('\n--- Polling Pattern ---');
async function poll(fn, { interval = 1000, timeout = 30000, condition } = {}) {
const startTime = Date.now();
while (true) {
const result = await fn();
if (condition(result)) {
return result;
}
if (Date.now() - startTime > timeout) {
throw new Error('Polling timeout');
}
await delay(interval);
}
}
// Exponential polling
async function pollWithBackoff(fn, options = {}) {
const {
initialInterval = 1000,
maxInterval = 30000,
factor = 1.5,
timeout = 60000,
condition = (result) => result.done,
} = options;
const startTime = Date.now();
let interval = initialInterval;
while (true) {
const result = await fn();
if (condition(result)) {
return result;
}
if (Date.now() - startTime > timeout) {
throw new Error('Polling timeout');
}
await delay(interval);
interval = Math.min(interval * factor, maxInterval);
}
}
// =============================================================================
// 11. QUEUE PATTERN
// =============================================================================
console.log('\n--- Queue Pattern ---');
class AsyncQueue {
constructor(concurrency = 1) {
this.concurrency = concurrency;
this.running = 0;
this.queue = [];
}
enqueue(task) {
return new Promise((resolve, reject) => {
this.queue.push({ task, resolve, reject });
this.processQueue();
});
}
async processQueue() {
while (this.running < this.concurrency && this.queue.length > 0) {
const { task, resolve, reject } = this.queue.shift();
this.running++;
try {
const result = await task();
resolve(result);
} catch (error) {
reject(error);
} finally {
this.running--;
this.processQueue();
}
}
}
get length() {
return this.queue.length;
}
get pending() {
return this.running;
}
}
// =============================================================================
// 12. MEMOIZATION WITH ASYNC
// =============================================================================
console.log('\n--- Async Memoization ---');
function memoizeAsync(fn, keyFn = (...args) => JSON.stringify(args)) {
const cache = new Map();
return async function (...args) {
const key = keyFn(...args);
if (cache.has(key)) {
console.log('Cache hit:', key);
return cache.get(key);
}
console.log('Cache miss:', key);
const result = await fn.apply(this, args);
cache.set(key, result);
return result;
};
}
// With TTL
function memoizeAsyncWithTTL(fn, ttl = 5000) {
const cache = new Map();
return async function (...args) {
const key = JSON.stringify(args);
const cached = cache.get(key);
if (cached && Date.now() - cached.timestamp < ttl) {
return cached.value;
}
const value = await fn.apply(this, args);
cache.set(key, { value, timestamp: Date.now() });
return value;
};
}
// =============================================================================
// RUN DEMONSTRATIONS
// =============================================================================
async function runDemos() {
console.log('\n========= Running Demonstrations =========\n');
console.log('1. Sequential vs Parallel:');
await processSequential([1, 2, 3]);
await processParallel([1, 2, 3]);
console.log('\n2. Batched:');
await processBatched([1, 2, 3, 4, 5], 2);
console.log('\n3. Pipeline:');
await demoPipeline();
console.log('\n4. Debounce:');
demoDebounce();
console.log('\n5. Timeout:');
await demoTimeout();
console.log('\n========= Demonstrations Complete =========');
}
// Run demos
runDemos();