javascript
examples
examples.js⚡javascript
/**
* ========================================
* 9.1 CALLBACKS - CODE EXAMPLES
* ========================================
*/
/**
* EXAMPLE 1: Basic Callback
* A function that takes another function as argument
*/
console.log('--- Example 1: Basic Callback ---');
function greet(name, callback) {
console.log(`Hello, ${name}!`);
callback();
}
function afterGreeting() {
console.log('Nice to meet you!');
}
greet('Alice', afterGreeting);
// With anonymous function
greet('Bob', function () {
console.log('How are you?');
});
// With arrow function
greet('Charlie', () => console.log('Welcome!'));
/**
* EXAMPLE 2: Synchronous Callbacks
* Callbacks that execute immediately
*/
console.log('\n--- Example 2: Synchronous Callbacks ---');
const numbers = [1, 2, 3, 4, 5];
// forEach - synchronous callback
console.log('forEach:');
numbers.forEach((num, index) => {
console.log(` Index ${index}: ${num}`);
});
// map - returns new array
const doubled = numbers.map((n) => n * 2);
console.log('Doubled:', doubled);
// filter - returns filtered array
const evens = numbers.filter((n) => n % 2 === 0);
console.log('Evens:', evens);
// reduce - accumulates value
const sum = numbers.reduce((acc, n) => acc + n, 0);
console.log('Sum:', sum);
// find - returns first match
const found = numbers.find((n) => n > 3);
console.log('First > 3:', found);
/**
* EXAMPLE 3: setTimeout - Asynchronous Callback
*/
console.log('\n--- Example 3: setTimeout ---');
console.log('Before setTimeout');
setTimeout(() => {
console.log('Inside setTimeout (1 second later)');
}, 1000);
console.log('After setTimeout (runs immediately!)');
// Zero delay still async
setTimeout(() => {
console.log('Zero delay - but still async!');
}, 0);
console.log('This runs before zero delay!');
/**
* EXAMPLE 4: setInterval - Repeated Callbacks
*/
console.log('\n--- Example 4: setInterval ---');
let count = 0;
const maxCount = 3;
const intervalId = setInterval(() => {
count++;
console.log(`Interval tick #${count}`);
if (count >= maxCount) {
clearInterval(intervalId);
console.log('Interval cleared!');
}
}, 500);
/**
* EXAMPLE 5: Callback with Parameters
*/
console.log('\n--- Example 5: Callback with Parameters ---');
function fetchData(url, callback) {
console.log(`Fetching data from: ${url}`);
// Simulate async operation
setTimeout(() => {
const data = { id: 1, name: 'Sample Data', url };
callback(data);
}, 100);
}
fetchData('https://api.example.com/users', (result) => {
console.log('Received:', result);
});
/**
* EXAMPLE 6: Error-First Callback Pattern
*/
console.log('\n--- Example 6: Error-First Callbacks ---');
function divideAsync(a, b, callback) {
setTimeout(() => {
if (b === 0) {
callback(new Error('Division by zero'), null);
} else {
callback(null, a / b);
}
}, 100);
}
// Success case
divideAsync(10, 2, (error, result) => {
if (error) {
console.error('Error:', error.message);
return;
}
console.log('10 / 2 =', result);
});
// Error case
divideAsync(10, 0, (error, result) => {
if (error) {
console.error('Error:', error.message);
return;
}
console.log('Result:', result);
});
/**
* EXAMPLE 7: Multiple Callbacks (Success/Error)
*/
console.log('\n--- Example 7: Success/Error Callbacks ---');
function validateUser(user, onSuccess, onError) {
setTimeout(() => {
if (!user.name) {
onError(new Error('Name is required'));
} else if (!user.email) {
onError(new Error('Email is required'));
} else if (!user.email.includes('@')) {
onError(new Error('Invalid email format'));
} else {
onSuccess({ ...user, validated: true });
}
}, 100);
}
// Valid user
validateUser(
{ name: 'Alice', email: 'alice@example.com' },
(user) => console.log('Valid user:', user),
(error) => console.error('Validation failed:', error.message)
);
// Invalid user
validateUser(
{ name: 'Bob', email: 'invalid' },
(user) => console.log('Valid user:', user),
(error) => console.error('Validation failed:', error.message)
);
/**
* EXAMPLE 8: Callback Hell (Anti-pattern)
*/
console.log('\n--- Example 8: Callback Hell ---');
// Simulated async operations
function getUser(id, callback) {
setTimeout(() => callback(null, { id, name: 'John' }), 50);
}
function getOrders(userId, callback) {
setTimeout(() => callback(null, [{ id: 1, product: 'Laptop' }]), 50);
}
function getOrderDetails(orderId, callback) {
setTimeout(() => callback(null, { id: orderId, price: 999 }), 50);
}
// ❌ Callback Hell - hard to read and maintain
getUser(1, (error, user) => {
if (error) {
console.error(error);
return;
}
console.log('User:', user);
getOrders(user.id, (error, orders) => {
if (error) {
console.error(error);
return;
}
console.log('Orders:', orders);
getOrderDetails(orders[0].id, (error, details) => {
if (error) {
console.error(error);
return;
}
console.log('Details:', details);
});
});
});
/**
* EXAMPLE 9: Avoiding Callback Hell - Named Functions
*/
console.log('\n--- Example 9: Named Functions Solution ---');
function handleUser(error, user) {
if (error) {
console.error('User error:', error);
return;
}
console.log('Got user:', user.name);
getOrders(user.id, handleOrders);
}
function handleOrders(error, orders) {
if (error) {
console.error('Orders error:', error);
return;
}
console.log('Got orders:', orders.length);
getOrderDetails(orders[0].id, handleDetails);
}
function handleDetails(error, details) {
if (error) {
console.error('Details error:', error);
return;
}
console.log('Got details:', details);
}
// ✅ Clean, flat structure
getUser(1, handleUser);
/**
* EXAMPLE 10: Callback Wrapper Utility
*/
console.log('\n--- Example 10: Callback Wrapper ---');
// Utility to handle error-first callbacks
function handleCallback(onSuccess, onError = console.error) {
return function (error, result) {
if (error) {
onError(error);
} else {
onSuccess(result);
}
};
}
// Usage
getUser(
1,
handleCallback(
(user) => console.log('Wrapped callback - User:', user),
(error) => console.error('Wrapped callback - Error:', error)
)
);
/**
* EXAMPLE 11: Parallel Execution with Callbacks
*/
console.log('\n--- Example 11: Parallel Execution ---');
function fetchMultiple(urls, callback) {
const results = [];
let completed = 0;
let hasError = false;
urls.forEach((url, index) => {
// Simulate fetch
setTimeout(() => {
if (hasError) return;
if (url.includes('error')) {
hasError = true;
callback(new Error(`Failed: ${url}`), null);
return;
}
results[index] = `Data from ${url}`;
completed++;
if (completed === urls.length) {
callback(null, results);
}
}, Math.random() * 200);
});
}
fetchMultiple(
['/api/users', '/api/posts', '/api/comments'],
(error, results) => {
if (error) {
console.error('Parallel error:', error.message);
return;
}
console.log('Parallel results:', results);
}
);
/**
* EXAMPLE 12: Sequential Execution with Callbacks
*/
console.log('\n--- Example 12: Sequential Execution ---');
function runSequence(tasks, finalCallback) {
let index = 0;
const results = [];
function next(error, result) {
if (error) {
finalCallback(error, null);
return;
}
if (result !== undefined) {
results.push(result);
}
if (index >= tasks.length) {
finalCallback(null, results);
return;
}
const task = tasks[index++];
task(next);
}
next();
}
// Define tasks
const tasks = [
(cb) => setTimeout(() => cb(null, 'Task 1 done'), 100),
(cb) => setTimeout(() => cb(null, 'Task 2 done'), 50),
(cb) => setTimeout(() => cb(null, 'Task 3 done'), 75),
];
runSequence(tasks, (error, results) => {
if (error) {
console.error('Sequence error:', error);
return;
}
console.log('Sequence results:', results);
});
/**
* EXAMPLE 13: Debounce Function
*/
console.log('\n--- Example 13: Debounce ---');
function debounce(func, delay) {
let timeoutId;
return function (...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
// Simulated search function
const search = debounce((query) => {
console.log(`Searching for: ${query}`);
}, 300);
// Rapid calls - only last one executes
search('a');
search('ap');
search('app');
search('appl');
search('apple'); // Only this runs after 300ms
/**
* EXAMPLE 14: Throttle Function
*/
console.log('\n--- Example 14: Throttle ---');
function throttle(func, limit) {
let inThrottle = false;
return function (...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => {
inThrottle = false;
}, limit);
}
};
}
// Simulated scroll handler
const onScroll = throttle((position) => {
console.log(`Scroll position: ${position}`);
}, 200);
// Simulate rapid scroll events
for (let i = 0; i < 10; i++) {
onScroll(i * 100);
}
/**
* EXAMPLE 15: Once Function
*/
console.log('\n--- Example 15: Once (Single Execution) ---');
function once(func) {
let called = false;
let result;
return function (...args) {
if (!called) {
called = true;
result = func.apply(this, args);
}
return result;
};
}
const initialize = once(() => {
console.log('Initialization complete!');
return { initialized: true };
});
console.log(initialize()); // Runs function
console.log(initialize()); // Returns cached result
console.log(initialize()); // Returns cached result
/**
* EXAMPLE 16: Retry with Callbacks
*/
console.log('\n--- Example 16: Retry Pattern ---');
function retry(operation, maxAttempts, delay, callback) {
let attempts = 0;
function attempt() {
attempts++;
console.log(`Attempt ${attempts}/${maxAttempts}`);
operation((error, result) => {
if (error) {
if (attempts >= maxAttempts) {
callback(new Error(`Failed after ${maxAttempts} attempts`), null);
} else {
setTimeout(attempt, delay);
}
} else {
callback(null, result);
}
});
}
attempt();
}
// Simulated flaky operation
let failCount = 0;
function flakyOperation(callback) {
failCount++;
if (failCount < 3) {
callback(new Error('Temporary failure'), null);
} else {
callback(null, 'Success!');
}
}
retry(flakyOperation, 5, 100, (error, result) => {
if (error) {
console.error('Retry failed:', error.message);
} else {
console.log('Retry succeeded:', result);
}
});
/**
* EXAMPLE 17: Event Emitter Pattern
*/
console.log('\n--- Example 17: Event Emitter ---');
class SimpleEventEmitter {
constructor() {
this.events = {};
}
on(event, callback) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(callback);
return () => this.off(event, callback);
}
off(event, callback) {
if (this.events[event]) {
this.events[event] = this.events[event].filter((cb) => cb !== callback);
}
}
emit(event, ...args) {
if (this.events[event]) {
this.events[event].forEach((callback) => {
callback(...args);
});
}
}
once(event, callback) {
const wrapper = (...args) => {
callback(...args);
this.off(event, wrapper);
};
this.on(event, wrapper);
}
}
const emitter = new SimpleEventEmitter();
emitter.on('message', (msg) => console.log('Handler 1:', msg));
emitter.on('message', (msg) => console.log('Handler 2:', msg));
emitter.once('connect', () => console.log('Connected! (only once)'));
emitter.emit('message', 'Hello!');
emitter.emit('connect');
emitter.emit('connect'); // No output - handler removed
/**
* EXAMPLE 18: Callback Queue
*/
console.log('\n--- Example 18: Callback Queue ---');
class CallbackQueue {
constructor(concurrency = 1) {
this.concurrency = concurrency;
this.running = 0;
this.queue = [];
}
add(task, callback) {
this.queue.push({ task, callback });
this.process();
}
process() {
while (this.running < this.concurrency && this.queue.length > 0) {
const { task, callback } = this.queue.shift();
this.running++;
task((error, result) => {
this.running--;
callback(error, result);
this.process();
});
}
}
}
const queue = new CallbackQueue(2); // Max 2 concurrent
for (let i = 1; i <= 5; i++) {
queue.add(
(cb) => {
console.log(`Task ${i} started`);
setTimeout(() => cb(null, `Task ${i} result`), 100);
},
(err, result) => {
console.log(`Task ${i} completed:`, result);
}
);
}
/**
* EXAMPLE 19: Memoization with Callbacks
*/
console.log('\n--- Example 19: Async Memoization ---');
function memoizeAsync(func) {
const cache = new Map();
const pending = new Map();
return function (key, callback) {
// Return cached result
if (cache.has(key)) {
console.log('Cache hit for:', key);
setTimeout(() => callback(null, cache.get(key)), 0);
return;
}
// If request is in progress, queue callback
if (pending.has(key)) {
pending.get(key).push(callback);
return;
}
// Start new request
pending.set(key, [callback]);
func(key, (error, result) => {
const callbacks = pending.get(key);
pending.delete(key);
if (!error) {
cache.set(key, result);
}
callbacks.forEach((cb) => cb(error, result));
});
};
}
// Simulated expensive operation
function fetchUser(id, callback) {
console.log('Fetching user:', id);
setTimeout(() => callback(null, { id, name: `User ${id}` }), 100);
}
const memoizedFetch = memoizeAsync(fetchUser);
// First call - fetches
memoizedFetch(1, (err, user) => console.log('Result 1:', user));
// Second call (same key) - uses cache
setTimeout(() => {
memoizedFetch(1, (err, user) => console.log('Result 2:', user));
}, 200);
/**
* EXAMPLE 20: Timeout Wrapper
*/
console.log('\n--- Example 20: Timeout Wrapper ---');
function withTimeout(operation, timeout) {
return function (callback) {
let completed = false;
const timeoutId = setTimeout(() => {
if (!completed) {
completed = true;
callback(new Error('Operation timed out'), null);
}
}, timeout);
operation((error, result) => {
if (!completed) {
completed = true;
clearTimeout(timeoutId);
callback(error, result);
}
});
};
}
// Slow operation
function slowOperation(callback) {
setTimeout(() => callback(null, 'Done!'), 500);
}
// Fast operation
function fastOperation(callback) {
setTimeout(() => callback(null, 'Quick!'), 50);
}
// With timeout
const timedSlow = withTimeout(slowOperation, 200);
const timedFast = withTimeout(fastOperation, 200);
timedSlow((error, result) => {
console.log('Slow result:', error ? error.message : result);
});
timedFast((error, result) => {
console.log('Fast result:', error ? error.message : result);
});
console.log('\n========================================');
console.log('End of Callback Examples');
console.log('========================================');