Skip to content

Commit 8f28c56

Browse files
committed
debounce and throttle
1 parent d084296 commit 8f28c56

File tree

4 files changed

+220
-0
lines changed

4 files changed

+220
-0
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Debounce and Throttle
2+
3+
Utility functions to control how often a function executes during frequent events.
4+
5+
## Overview
6+
7+
**Debounce** - Waits until user stops action before executing (e.g., search input, auto-save)
8+
**Throttle** - Limits execution to once per time interval (e.g., scroll, resize)
9+
10+
## Files
11+
12+
- `debounce.js` - 3 debounce variations
13+
- `throttle.js` - 4 throttle variations
14+
- `combined-usage.js` - Real-world examples
15+
16+
## Quick Examples
17+
18+
### Debounce
19+
```javascript
20+
const handleSearch = debounce((query) => {
21+
// API call after user stops typing
22+
fetch(`/api/search?q=${query}`);
23+
}, 300);
24+
25+
inputElement.addEventListener('input', e => handleSearch(e.target.value));
26+
```
27+
28+
### Throttle
29+
```javascript
30+
const handleScroll = throttle(() => {
31+
// Update every 300ms during scroll
32+
updateScrollBar();
33+
}, 300);
34+
35+
window.addEventListener('scroll', handleScroll);
36+
```
37+
38+
## Use Cases
39+
40+
**Debounce:** search input, auto-save, form validation
41+
**Throttle:** scroll events, window resize, animations
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Search with debounce - waits for user to stop typing
2+
class SearchBox {
3+
constructor(inputSelector, onSearch) {
4+
this.input = document.querySelector(inputSelector);
5+
this.onSearch = onSearch;
6+
this.debouncedSearch = debounce((query) => {
7+
if (query.length > 2) this.onSearch(query);
8+
}, 300);
9+
this.input.addEventListener('input', e => this.debouncedSearch(e.target.value));
10+
}
11+
}
12+
13+
// Auto-save form after user stops typing
14+
class AutoSaveForm {
15+
constructor(formSelector, saveUrl) {
16+
this.form = document.querySelector(formSelector);
17+
this.saveUrl = saveUrl;
18+
this.debouncedSave = debounce(() => this.save(), 1000);
19+
this.form.addEventListener('input', () => this.debouncedSave());
20+
}
21+
22+
async save() {
23+
const data = new FormData(this.form);
24+
try {
25+
await fetch(this.saveUrl, {
26+
method: 'POST',
27+
body: new URLSearchParams(data)
28+
});
29+
console.log('Saved');
30+
} catch (e) {
31+
console.error('Save failed', e);
32+
}
33+
}
34+
}
35+
36+
// Handle window resize with throttle
37+
class ResponsiveLayout {
38+
constructor() {
39+
this.throttledResize = throttle(() => this.handleResize(), 500);
40+
window.addEventListener('resize', this.throttledResize);
41+
}
42+
43+
handleResize() {
44+
const width = window.innerWidth;
45+
if (width < 768) {
46+
document.body.classList.add('mobile');
47+
} else {
48+
document.body.classList.remove('mobile');
49+
}
50+
}
51+
}
52+
53+
// Scroll detection with throttle
54+
class ScrollHandler {
55+
constructor() {
56+
this.throttledScroll = throttle(() => {
57+
const pos = window.scrollY;
58+
const docHeight = document.documentElement.scrollHeight;
59+
if (pos + window.innerHeight >= docHeight - 100) {
60+
this.loadMore();
61+
}
62+
}, 300);
63+
window.addEventListener('scroll', this.throttledScroll);
64+
}
65+
66+
loadMore() {
67+
console.log('Load more content');
68+
}
69+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Basic debounce - delays function execution until after user stops action
2+
function debounce(func, wait) {
3+
let timeout;
4+
return function(...args) {
5+
clearTimeout(timeout);
6+
timeout = setTimeout(() => func.apply(this, args), wait);
7+
};
8+
}
9+
10+
// Debounce with cancel method
11+
function debounceWithCancel(func, wait) {
12+
let timeout;
13+
const debounced = function(...args) {
14+
clearTimeout(timeout);
15+
timeout = setTimeout(() => func.apply(this, args), wait);
16+
};
17+
debounced.cancel = () => clearTimeout(timeout);
18+
return debounced;
19+
}
20+
21+
// Debounce with flush - execute immediately
22+
function debounceWithFlush(func, wait) {
23+
let timeout, lastArgs, lastThis;
24+
const debounced = function(...args) {
25+
lastArgs = args;
26+
lastThis = this;
27+
clearTimeout(timeout);
28+
timeout = setTimeout(() => func.apply(lastThis, lastArgs), wait);
29+
};
30+
debounced.flush = () => {
31+
if (timeout) {
32+
clearTimeout(timeout);
33+
func.apply(lastThis, lastArgs);
34+
}
35+
};
36+
return debounced;
37+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Basic throttle - limit function execution to once per interval
2+
function throttle(func, limit) {
3+
let inThrottle;
4+
return function(...args) {
5+
if (!inThrottle) {
6+
func.apply(this, args);
7+
inThrottle = true;
8+
setTimeout(() => inThrottle = false, limit);
9+
}
10+
};
11+
}
12+
13+
// Throttle with timestamp - more precise timing
14+
function throttleWithTimestamp(func, limit) {
15+
let lastRan;
16+
return function(...args) {
17+
if (!lastRan) {
18+
func.apply(this, args);
19+
lastRan = Date.now();
20+
} else {
21+
const remaining = limit - (Date.now() - lastRan);
22+
if (remaining <= 0) {
23+
func.apply(this, args);
24+
lastRan = Date.now();
25+
}
26+
}
27+
};
28+
}
29+
30+
// Throttle with leading and trailing options
31+
function throttleAdvanced(func, wait, options = {}) {
32+
const { leading = true, trailing = false } = options;
33+
let timeout, previous = 0, lastArgs, lastThis;
34+
35+
const throttled = function(...args) {
36+
const now = Date.now();
37+
if (!previous && !leading) previous = now;
38+
39+
const remaining = wait - (now - previous);
40+
lastArgs = args;
41+
lastThis = this;
42+
43+
if (remaining <= 0) {
44+
clearTimeout(timeout);
45+
timeout = null;
46+
previous = now;
47+
func.apply(this, args);
48+
} else if (!timeout && trailing) {
49+
timeout = setTimeout(() => {
50+
previous = leading ? Date.now() : 0;
51+
timeout = null;
52+
func.apply(lastThis, lastArgs);
53+
}, remaining);
54+
}
55+
};
56+
throttled.cancel = () => {
57+
clearTimeout(timeout);
58+
previous = 0;
59+
};
60+
return throttled;
61+
}
62+
63+
// Throttle using requestAnimationFrame for smooth animation
64+
function throttleRAF(func) {
65+
let frameId = null;
66+
return function(...args) {
67+
if (frameId) return;
68+
frameId = requestAnimationFrame(() => {
69+
func.apply(this, args);
70+
frameId = null;
71+
});
72+
};
73+
}

0 commit comments

Comments
 (0)