diff --git a/Modern Development/Debounce and Throttle/README.md b/Modern Development/Debounce and Throttle/README.md new file mode 100644 index 0000000000..c80b6be23d --- /dev/null +++ b/Modern Development/Debounce and Throttle/README.md @@ -0,0 +1,41 @@ +# Debounce and Throttle + +Utility functions to control how often a function executes during frequent events. + +## Overview + +**Debounce** - Waits until user stops action before executing (e.g., search input, auto-save) +**Throttle** - Limits execution to once per time interval (e.g., scroll, resize) + +## Files + +- `debounce.js` - 3 debounce variations +- `throttle.js` - 4 throttle variations +- `combined-usage.js` - Real-world examples + +## Quick Examples + +### Debounce +```javascript +const handleSearch = debounce((query) => { + // API call after user stops typing + fetch(`/api/search?q=${query}`); +}, 300); + +inputElement.addEventListener('input', e => handleSearch(e.target.value)); +``` + +### Throttle +```javascript +const handleScroll = throttle(() => { + // Update every 300ms during scroll + updateScrollBar(); +}, 300); + +window.addEventListener('scroll', handleScroll); +``` + +## Use Cases + +**Debounce:** search input, auto-save, form validation +**Throttle:** scroll events, window resize, animations diff --git a/Modern Development/Debounce and Throttle/combined-usage.js b/Modern Development/Debounce and Throttle/combined-usage.js new file mode 100644 index 0000000000..12e0f59abf --- /dev/null +++ b/Modern Development/Debounce and Throttle/combined-usage.js @@ -0,0 +1,69 @@ +// Search with debounce - waits for user to stop typing +class SearchBox { + constructor(inputSelector, onSearch) { + this.input = document.querySelector(inputSelector); + this.onSearch = onSearch; + this.debouncedSearch = debounce((query) => { + if (query.length > 2) this.onSearch(query); + }, 300); + this.input.addEventListener('input', e => this.debouncedSearch(e.target.value)); + } +} + +// Auto-save form after user stops typing +class AutoSaveForm { + constructor(formSelector, saveUrl) { + this.form = document.querySelector(formSelector); + this.saveUrl = saveUrl; + this.debouncedSave = debounce(() => this.save(), 1000); + this.form.addEventListener('input', () => this.debouncedSave()); + } + + async save() { + const data = new FormData(this.form); + try { + await fetch(this.saveUrl, { + method: 'POST', + body: new URLSearchParams(data) + }); + console.log('Saved'); + } catch (e) { + console.error('Save failed', e); + } + } +} + +// Handle window resize with throttle +class ResponsiveLayout { + constructor() { + this.throttledResize = throttle(() => this.handleResize(), 500); + window.addEventListener('resize', this.throttledResize); + } + + handleResize() { + const width = window.innerWidth; + if (width < 768) { + document.body.classList.add('mobile'); + } else { + document.body.classList.remove('mobile'); + } + } +} + +// Scroll detection with throttle +class ScrollHandler { + constructor() { + this.throttledScroll = throttle(() => { + const pos = window.scrollY; + const docHeight = document.documentElement.scrollHeight; + if (pos + window.innerHeight >= docHeight - 100) { + this.loadMore(); + } + }, 300); + window.addEventListener('scroll', this.throttledScroll); + } + + loadMore() { + console.log('Load more content'); + } +} diff --git a/Modern Development/Debounce and Throttle/debounce.js b/Modern Development/Debounce and Throttle/debounce.js new file mode 100644 index 0000000000..7d5a19ebd3 --- /dev/null +++ b/Modern Development/Debounce and Throttle/debounce.js @@ -0,0 +1,37 @@ +// Basic debounce - delays function execution until after user stops action +function debounce(func, wait) { + let timeout; + return function(...args) { + clearTimeout(timeout); + timeout = setTimeout(() => func.apply(this, args), wait); + }; +} + +// Debounce with cancel method +function debounceWithCancel(func, wait) { + let timeout; + const debounced = function(...args) { + clearTimeout(timeout); + timeout = setTimeout(() => func.apply(this, args), wait); + }; + debounced.cancel = () => clearTimeout(timeout); + return debounced; +} + +// Debounce with flush - execute immediately +function debounceWithFlush(func, wait) { + let timeout, lastArgs, lastThis; + const debounced = function(...args) { + lastArgs = args; + lastThis = this; + clearTimeout(timeout); + timeout = setTimeout(() => func.apply(lastThis, lastArgs), wait); + }; + debounced.flush = () => { + if (timeout) { + clearTimeout(timeout); + func.apply(lastThis, lastArgs); + } + }; + return debounced; +} diff --git a/Modern Development/Debounce and Throttle/throttle.js b/Modern Development/Debounce and Throttle/throttle.js new file mode 100644 index 0000000000..dba497dd64 --- /dev/null +++ b/Modern Development/Debounce and Throttle/throttle.js @@ -0,0 +1,73 @@ +// Basic throttle - limit function execution to once per interval +function throttle(func, limit) { + let inThrottle; + return function(...args) { + if (!inThrottle) { + func.apply(this, args); + inThrottle = true; + setTimeout(() => inThrottle = false, limit); + } + }; +} + +// Throttle with timestamp - more precise timing +function throttleWithTimestamp(func, limit) { + let lastRan; + return function(...args) { + if (!lastRan) { + func.apply(this, args); + lastRan = Date.now(); + } else { + const remaining = limit - (Date.now() - lastRan); + if (remaining <= 0) { + func.apply(this, args); + lastRan = Date.now(); + } + } + }; +} + +// Throttle with leading and trailing options +function throttleAdvanced(func, wait, options = {}) { + const { leading = true, trailing = false } = options; + let timeout, previous = 0, lastArgs, lastThis; + + const throttled = function(...args) { + const now = Date.now(); + if (!previous && !leading) previous = now; + + const remaining = wait - (now - previous); + lastArgs = args; + lastThis = this; + + if (remaining <= 0) { + clearTimeout(timeout); + timeout = null; + previous = now; + func.apply(this, args); + } else if (!timeout && trailing) { + timeout = setTimeout(() => { + previous = leading ? Date.now() : 0; + timeout = null; + func.apply(lastThis, lastArgs); + }, remaining); + } + }; + throttled.cancel = () => { + clearTimeout(timeout); + previous = 0; + }; + return throttled; +} + +// Throttle using requestAnimationFrame for smooth animation +function throttleRAF(func) { + let frameId = null; + return function(...args) { + if (frameId) return; + frameId = requestAnimationFrame(() => { + func.apply(this, args); + frameId = null; + }); + }; +}