javascript
exercises
exercises.js⚡javascript
/**
* ============================================================
* 9.5 TIMERS AND INTERVALS - EXERCISES
* ============================================================
*
* Complete each exercise by implementing the timer functions.
* Test your solutions by running this file.
*/
// Helper
function delay(ms, value) {
return new Promise((resolve) => setTimeout(() => resolve(value), ms));
}
/**
* EXERCISE 1: Basic Delay Promise
*
* Create a function 'wait' that:
* - Takes a number of milliseconds
* - Returns a promise that resolves after that time
* - Resolves with the message "Done waiting"
*/
// TODO: Implement wait
function wait(ms) {
// Your code here
}
// Test
// wait(100).then(msg => console.log('Exercise 1:', msg));
/*
* SOLUTION 1:
*
* function wait(ms) {
* return new Promise(resolve => {
* setTimeout(() => resolve("Done waiting"), ms);
* });
* }
*/
/**
* EXERCISE 2: Timeout with Value
*
* Create a function 'delayedValue' that:
* - Takes (ms, value)
* - Returns a promise that resolves with value after ms
*/
// TODO: Implement delayedValue
function delayedValue(ms, value) {
// Your code here
}
// Test
// delayedValue(100, { name: 'test' }).then(v => console.log('Exercise 2:', v));
/*
* SOLUTION 2:
*
* function delayedValue(ms, value) {
* return new Promise(resolve => setTimeout(() => resolve(value), ms));
* }
*/
/**
* EXERCISE 3: Timeout Reject
*
* Create a function 'rejectAfter' that:
* - Takes (ms, message)
* - Returns a promise that rejects with Error(message) after ms
*/
// TODO: Implement rejectAfter
function rejectAfter(ms, message) {
// Your code here
}
// Test
// rejectAfter(100, 'Timeout!').catch(e => console.log('Exercise 3:', e.message));
/*
* SOLUTION 3:
*
* function rejectAfter(ms, message) {
* return new Promise((_, reject) => {
* setTimeout(() => reject(new Error(message)), ms);
* });
* }
*/
/**
* EXERCISE 4: Cancellable Delay
*
* Create a function 'cancellableDelay' that:
* - Takes (ms, signal) where signal is an AbortSignal
* - Returns a promise that resolves after ms
* - Rejects immediately if signal is aborted
* - Cleans up the timeout when aborted
*/
// TODO: Implement cancellableDelay
function cancellableDelay(ms, signal) {
// Your code here
}
// Test
// const controller = new AbortController();
// cancellableDelay(500, controller.signal)
// .then(() => console.log('Exercise 4: Completed'))
// .catch(e => console.log('Exercise 4:', e.message));
// setTimeout(() => controller.abort(), 100);
/*
* SOLUTION 4:
*
* function cancellableDelay(ms, signal) {
* return new Promise((resolve, reject) => {
* const timeoutId = setTimeout(resolve, ms);
*
* signal?.addEventListener('abort', () => {
* clearTimeout(timeoutId);
* reject(new Error('Aborted'));
* });
* });
* }
*/
/**
* EXERCISE 5: Simple Debounce
*
* Create a function 'debounce' that:
* - Takes (fn, wait)
* - Returns a debounced function
* - Only calls fn after wait ms of no calls
*/
// TODO: Implement debounce
function debounce(fn, wait) {
// Your code here
}
// Test
// const debouncedLog = debounce(x => console.log('Exercise 5:', x), 100);
// debouncedLog(1); debouncedLog(2); debouncedLog(3);
// // Should only log "3" after 100ms
/*
* SOLUTION 5:
*
* function debounce(fn, wait) {
* let timeoutId;
* return function(...args) {
* clearTimeout(timeoutId);
* timeoutId = setTimeout(() => fn.apply(this, args), wait);
* };
* }
*/
/**
* EXERCISE 6: Debounce with Immediate
*
* Create a function 'debounceImmediate' that:
* - Takes (fn, wait, immediate = false)
* - If immediate is true, calls fn on first call
* - Still waits for quiet period before next call
*/
// TODO: Implement debounceImmediate
function debounceImmediate(fn, wait, immediate = false) {
// Your code here
}
// Test
// const immediateDeb = debounceImmediate(x => console.log('Exercise 6:', x), 100, true);
// immediateDeb('first'); // Logs immediately
// immediateDeb('second'); // Ignored
// immediateDeb('third'); // Ignored
// // After 100ms of quiet, next call will log immediately again
/*
* SOLUTION 6:
*
* function debounceImmediate(fn, wait, immediate = false) {
* let timeoutId;
* let canCall = true;
*
* return function(...args) {
* if (immediate && canCall) {
* fn.apply(this, args);
* canCall = false;
* }
*
* clearTimeout(timeoutId);
* timeoutId = setTimeout(() => {
* if (!immediate) fn.apply(this, args);
* canCall = true;
* }, wait);
* };
* }
*/
/**
* EXERCISE 7: Simple Throttle
*
* Create a function 'throttle' that:
* - Takes (fn, limit)
* - Returns a throttled function
* - Only allows fn to be called once per limit ms
*/
// TODO: Implement throttle
function throttle(fn, limit) {
// Your code here
}
// Test
// const throttledLog = throttle(x => console.log('Exercise 7:', x), 100);
// throttledLog(1); // Logs
// throttledLog(2); // Ignored
// setTimeout(() => throttledLog(3), 150); // Logs
/*
* SOLUTION 7:
*
* function throttle(fn, limit) {
* let inThrottle = false;
* return function(...args) {
* if (!inThrottle) {
* fn.apply(this, args);
* inThrottle = true;
* setTimeout(() => inThrottle = false, limit);
* }
* };
* }
*/
/**
* EXERCISE 8: Throttle with Trailing
*
* Create a function 'throttleTrailing' that:
* - Takes (fn, limit)
* - Calls fn immediately on first call
* - Also calls fn with last arguments after limit if calls were made
*/
// TODO: Implement throttleTrailing
function throttleTrailing(fn, limit) {
// Your code here
}
// Test
// const trailingThrottle = throttleTrailing(x => console.log('Exercise 8:', x), 100);
// trailingThrottle('first'); // Logs immediately
// trailingThrottle('middle'); // Saved
// trailingThrottle('last'); // Saved, logs after 100ms
/*
* SOLUTION 8:
*
* function throttleTrailing(fn, limit) {
* let lastArgs = null;
* let timeoutId = null;
*
* return function(...args) {
* if (!timeoutId) {
* fn.apply(this, args);
* timeoutId = setTimeout(() => {
* if (lastArgs) {
* fn.apply(this, lastArgs);
* lastArgs = null;
* }
* timeoutId = null;
* }, limit);
* } else {
* lastArgs = args;
* }
* };
* }
*/
/**
* EXERCISE 9: Interval Counter
*
* Create a function 'countUp' that:
* - Takes (callback, intervalMs, count)
* - Calls callback with (currentCount) every intervalMs
* - Stops after count iterations
* - Returns a function to stop early
*/
// TODO: Implement countUp
function countUp(callback, intervalMs, count) {
// Your code here
}
// Test
// const stop = countUp(n => console.log('Exercise 9: Count', n), 100, 5);
// // Logs 1, 2, 3, 4, 5 then stops
// // Or: setTimeout(() => stop(), 250); // Stop early
/*
* SOLUTION 9:
*
* function countUp(callback, intervalMs, count) {
* let current = 0;
* const intervalId = setInterval(() => {
* current++;
* callback(current);
* if (current >= count) {
* clearInterval(intervalId);
* }
* }, intervalMs);
*
* return function stop() {
* clearInterval(intervalId);
* };
* }
*/
/**
* EXERCISE 10: Polling Function
*
* Create an async function 'pollUntil' that:
* - Takes (asyncFn, conditionFn, intervalMs, timeoutMs)
* - Calls asyncFn every intervalMs
* - Stops when conditionFn(result) returns true
* - Rejects if timeoutMs is exceeded
*/
// TODO: Implement pollUntil
async function pollUntil(asyncFn, conditionFn, intervalMs, timeoutMs) {
// Your code here
}
// Test
// let pollCount = 0;
// pollUntil(
// async () => ({ ready: ++pollCount >= 3, count: pollCount }),
// result => result.ready,
// 50,
// 1000
// ).then(r => console.log('Exercise 10:', r));
/*
* SOLUTION 10:
*
* async function pollUntil(asyncFn, conditionFn, intervalMs, timeoutMs) {
* const startTime = Date.now();
*
* while (Date.now() - startTime < timeoutMs) {
* const result = await asyncFn();
* if (conditionFn(result)) {
* return result;
* }
* await delay(intervalMs);
* }
*
* throw new Error('Polling timeout');
* }
*/
/**
* EXERCISE 11: Timer Manager
*
* Create a class 'TimerManager' that:
* - Has method 'setTimeout(callback, delay)' returning id
* - Has method 'setInterval(callback, interval)' returning id
* - Has method 'clear(id)' to cancel any timer
* - Has method 'clearAll()' to cancel all timers
* - Tracks all active timers
*/
// TODO: Implement TimerManager
class TimerManager {
// Your code here
}
// Test
// const tm = new TimerManager();
// const id1 = tm.setTimeout(() => console.log('Exercise 11: timeout'), 100);
// const id2 = tm.setInterval(() => console.log('Exercise 11: interval'), 50);
// setTimeout(() => tm.clear(id2), 150);
// console.log('Exercise 11: Active timers', tm.getActiveCount?.() || 'N/A');
/*
* SOLUTION 11:
*
* class TimerManager {
* constructor() {
* this.timers = new Map();
* this.nextId = 0;
* }
*
* setTimeout(callback, delay) {
* const id = ++this.nextId;
* const timerId = setTimeout(() => {
* this.timers.delete(id);
* callback();
* }, delay);
* this.timers.set(id, { type: 'timeout', timerId });
* return id;
* }
*
* setInterval(callback, interval) {
* const id = ++this.nextId;
* const timerId = setInterval(callback, interval);
* this.timers.set(id, { type: 'interval', timerId });
* return id;
* }
*
* clear(id) {
* const timer = this.timers.get(id);
* if (timer) {
* if (timer.type === 'timeout') {
* clearTimeout(timer.timerId);
* } else {
* clearInterval(timer.timerId);
* }
* this.timers.delete(id);
* }
* }
*
* clearAll() {
* for (const [id] of this.timers) {
* this.clear(id);
* }
* }
*
* getActiveCount() {
* return this.timers.size;
* }
* }
*/
/**
* EXERCISE 12: Rate Limiter
*
* Create a class 'RateLimiter' that:
* - Constructor takes (maxCalls, perMs)
* - Has async method 'acquire()' that waits if limit reached
* - Has method 'execute(asyncFn)' that rate-limits fn execution
*/
// TODO: Implement RateLimiter
class RateLimiter {
// Your code here
}
// Test
// const limiter = new RateLimiter(2, 200);
// Promise.all([1, 2, 3, 4].map(i =>
// limiter.execute(async () => {
// console.log(`Exercise 12: Call ${i} at ${Date.now() % 1000}ms`);
// return i;
// })
// )).then(results => console.log('Exercise 12: Results', results));
/*
* SOLUTION 12:
*
* class RateLimiter {
* constructor(maxCalls, perMs) {
* this.maxCalls = maxCalls;
* this.perMs = perMs;
* this.calls = [];
* }
*
* async acquire() {
* const now = Date.now();
* this.calls = this.calls.filter(time => now - time < this.perMs);
*
* if (this.calls.length >= this.maxCalls) {
* const waitTime = this.calls[0] + this.perMs - now;
* await delay(waitTime);
* return this.acquire();
* }
*
* this.calls.push(now);
* }
*
* async execute(asyncFn) {
* await this.acquire();
* return asyncFn();
* }
* }
*/
/**
* EXERCISE 13: Self-Correcting Interval
*
* Create a function 'accurateInterval' that:
* - Takes (callback, interval, count)
* - Calls callback(tickNumber, drift) count times
* - Corrects for drift to maintain accurate timing
* - Returns stop function
*/
// TODO: Implement accurateInterval
function accurateInterval(callback, interval, count) {
// Your code here
}
// Test
// const stopAccurate = accurateInterval(
// (tick, drift) => console.log(`Exercise 13: Tick ${tick}, drift ${drift}ms`),
// 100,
// 3
// );
/*
* SOLUTION 13:
*
* function accurateInterval(callback, interval, count) {
* let expected = Date.now() + interval;
* let tick = 0;
* let stopped = false;
*
* function step() {
* if (stopped || tick >= count) return;
*
* const drift = Date.now() - expected;
* tick++;
* callback(tick, drift);
*
* if (tick < count) {
* expected += interval;
* setTimeout(step, Math.max(0, interval - drift));
* }
* }
*
* setTimeout(step, interval);
*
* return function stop() {
* stopped = true;
* };
* }
*/
/**
* EXERCISE 14: Countdown Timer
*
* Create a class 'Countdown' that:
* - Constructor takes (seconds, onTick, onComplete)
* - Has methods: start(), pause(), resume(), reset()
* - onTick receives remaining seconds
* - onComplete is called when countdown reaches 0
*/
// TODO: Implement Countdown
class Countdown {
// Your code here
}
// Test
// const cd = new Countdown(
// 3,
// s => console.log(`Exercise 14: ${s}s remaining`),
// () => console.log('Exercise 14: Complete!')
// );
// cd.start();
/*
* SOLUTION 14:
*
* class Countdown {
* constructor(seconds, onTick, onComplete) {
* this.initial = seconds;
* this.remaining = seconds;
* this.onTick = onTick;
* this.onComplete = onComplete;
* this.intervalId = null;
* }
*
* start() {
* if (this.intervalId) return;
* this.onTick(this.remaining);
*
* this.intervalId = setInterval(() => {
* this.remaining--;
*
* if (this.remaining <= 0) {
* this.stop();
* this.onComplete();
* } else {
* this.onTick(this.remaining);
* }
* }, 1000);
* }
*
* pause() {
* this.stop();
* }
*
* resume() {
* if (this.remaining > 0) {
* this.start();
* }
* }
*
* reset() {
* this.stop();
* this.remaining = this.initial;
* }
*
* stop() {
* if (this.intervalId) {
* clearInterval(this.intervalId);
* this.intervalId = null;
* }
* }
* }
*/
/**
* EXERCISE 15: Task Scheduler
*
* Create a class 'Scheduler' that:
* - Has method 'schedule(name, callback, options)'
* options: { delay?, interval?, repeat? }
* - Has method 'cancel(name)'
* - Has method 'cancelAll()'
* - Has method 'list()' returning scheduled task names
* - Handles both one-time and repeating tasks
*/
// TODO: Implement Scheduler
class Scheduler {
// Your code here
}
// Test
// const sched = new Scheduler();
// sched.schedule('once', () => console.log('Exercise 15: Once'), { delay: 100 });
// sched.schedule('repeat', () => console.log('Exercise 15: Repeat'), { interval: 50, repeat: 3 });
// console.log('Exercise 15: Scheduled', sched.list());
/*
* SOLUTION 15:
*
* class Scheduler {
* constructor() {
* this.tasks = new Map();
* }
*
* schedule(name, callback, options = {}) {
* const { delay = 0, interval, repeat = Infinity } = options;
*
* if (interval) {
* let count = 0;
* const id = setInterval(() => {
* count++;
* callback(count);
* if (count >= repeat) {
* this.cancel(name);
* }
* }, interval);
* this.tasks.set(name, { type: 'interval', id });
* } else {
* const id = setTimeout(() => {
* callback();
* this.tasks.delete(name);
* }, delay);
* this.tasks.set(name, { type: 'timeout', id });
* }
* }
*
* cancel(name) {
* const task = this.tasks.get(name);
* if (task) {
* if (task.type === 'interval') {
* clearInterval(task.id);
* } else {
* clearTimeout(task.id);
* }
* this.tasks.delete(name);
* }
* }
*
* cancelAll() {
* for (const name of this.tasks.keys()) {
* this.cancel(name);
* }
* }
*
* list() {
* return Array.from(this.tasks.keys());
* }
* }
*/
// ============================================================
// RUN TESTS
// ============================================================
async function runAllTests() {
console.log('Testing Timer and Interval Exercises...\n');
// Uncomment individual tests above to verify solutions
}
// runAllTests();
console.log('Timer and Interval Exercises loaded.');
console.log('Uncomment tests to verify your solutions.');