Skip to content

Commit e70e9c0

Browse files
committed
feat: add throttle function example code in codeing exercise
1 parent 9ff38fe commit e70e9c0

File tree

2 files changed

+517
-0
lines changed

2 files changed

+517
-0
lines changed
Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
# Throttle Function
2+
3+
## Challenge
4+
Implement a throttle function that limits the rate at which a callback can execute, ensuring it runs at most once per specified time interval.
5+
6+
## Problem Description
7+
Throttling is a technique used to control the rate at which a function executes. Unlike debouncing (which delays execution until activity stops), throttling ensures a function executes at regular intervals during continuous activity.
8+
9+
### Real-World Use Cases
10+
- **Scroll Events**: Update UI elements (like progress bars or lazy-loading) at a controlled rate while scrolling
11+
- **Mouse Movement**: Track cursor position without overwhelming the browser with updates
12+
- **Window Resize**: Recalculate layout at regular intervals during resize
13+
- **API Rate Limiting**: Ensure requests don't exceed API rate limits
14+
- **Game Development**: Limit action frequency (e.g., shooting, jumping) to prevent spam
15+
- **Auto-save**: Save user input at regular intervals while they're typing
16+
- **Infinite Scroll**: Load more content at controlled intervals while scrolling
17+
18+
## Example
19+
20+
### Input
21+
```js
22+
function updateScrollPosition(position) {
23+
console.log(`Scroll position: ${position}px`);
24+
}
25+
26+
const throttledUpdate = throttle(updateScrollPosition, 1000);
27+
28+
// User scrolls continuously
29+
throttledUpdate(100); // t=0ms
30+
throttledUpdate(200); // t=100ms
31+
throttledUpdate(300); // t=200ms
32+
throttledUpdate(400); // t=300ms
33+
throttledUpdate(500); // t=1100ms
34+
throttledUpdate(600); // t=1200ms
35+
```
36+
37+
### Output
38+
```
39+
// Executes at regular 1000ms intervals
40+
Scroll position: 100px // t=0ms (immediate)
41+
Scroll position: 500px // t=1100ms (after 1000ms)
42+
Scroll position: 600px // t=2200ms (after another 1000ms)
43+
```
44+
45+
## Requirements
46+
1. The throttle function should accept a function and a time limit
47+
2. It should return a new function that limits execution rate
48+
3. The function should execute immediately on the first call (leading edge)
49+
4. Subsequent calls within the time limit should be controlled
50+
5. The function should preserve the correct `this` context and arguments
51+
6. Should handle both leading and trailing edge execution options
52+
53+
## Key Concepts
54+
- **Closures**: Maintaining state (lastExecuted, timeoutId) across function calls
55+
- **Higher-Order Functions**: Returning a function from a function
56+
- **setTimeout/clearTimeout**: Managing asynchronous delays
57+
- **Function Context**: Using `apply()` to preserve `this` binding
58+
- **Timestamps**: Using `Date.now()` to track execution timing
59+
60+
## Implementation Approaches
61+
62+
### 1. Leading Edge Throttle
63+
Executes immediately on first call, then ignores calls for the limit duration:
64+
```js
65+
function throttleLeading(func, limit) {
66+
let inThrottle = false;
67+
68+
return function(...args) {
69+
if (!inThrottle) {
70+
func.apply(this, args);
71+
inThrottle = true;
72+
setTimeout(() => inThrottle = false, limit);
73+
}
74+
};
75+
}
76+
```
77+
78+
### 2. Leading + Trailing Edge Throttle
79+
Executes immediately and also schedules a final execution:
80+
```js
81+
function throttle(func, limit) {
82+
let lastExecuted = 0;
83+
let timeoutId = null;
84+
85+
return function(...args) {
86+
const now = Date.now();
87+
const timeSinceLastExecution = now - lastExecuted;
88+
89+
if (timeSinceLastExecution >= limit) {
90+
lastExecuted = now;
91+
func.apply(this, args);
92+
} else {
93+
clearTimeout(timeoutId);
94+
const remainingTime = limit - timeSinceLastExecution;
95+
timeoutId = setTimeout(() => {
96+
lastExecuted = Date.now();
97+
func.apply(this, args);
98+
}, remainingTime);
99+
}
100+
};
101+
}
102+
```
103+
104+
## Throttle vs Debounce
105+
106+
| Feature | Throttle | Debounce |
107+
|---------|----------|----------|
108+
| **Execution Pattern** | At regular intervals during activity | Only after activity stops |
109+
| **Frequency** | Guaranteed execution every X ms | Single execution after silence |
110+
| **Use Case** | Continuous updates (scroll, resize) | Wait for completion (search input) |
111+
| **Example** | Update scroll position every 100ms | Search API call 500ms after typing stops |
112+
| **Behavior** | Executes periodically while active | Executes once when idle |
113+
114+
### Visual Comparison
115+
```
116+
User Activity: ████████████████████████████████
117+
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
118+
119+
Throttle: ✓ ✓ ✓ ✓
120+
(executes at regular intervals)
121+
122+
Debounce: ✓
123+
(executes only after activity stops)
124+
```
125+
126+
## Benefits
127+
- **Performance**: Reduces unnecessary function calls during high-frequency events
128+
- **Consistency**: Ensures predictable execution intervals
129+
- **User Experience**: Provides smooth, responsive updates without lag
130+
- **Resource Management**: Prevents overwhelming the browser or server
131+
- **Battery Life**: Reduces CPU usage on mobile devices
132+
133+
## Common Pitfalls
134+
135+
### 1. Losing `this` Context
136+
```js
137+
// ❌ Wrong: Arrow function loses context
138+
function throttle(func, limit) {
139+
return () => func(); // 'this' is lost
140+
}
141+
142+
// ✓ Correct: Use regular function and apply
143+
function throttle(func, limit) {
144+
return function(...args) {
145+
func.apply(this, args); // Preserves 'this'
146+
};
147+
}
148+
```
149+
150+
### 2. Not Clearing Previous Timeouts
151+
```js
152+
// ❌ Wrong: Multiple timeouts can stack up
153+
function throttle(func, limit) {
154+
return function() {
155+
setTimeout(() => func(), limit); // Creates new timeout every call
156+
};
157+
}
158+
159+
// ✓ Correct: Clear previous timeout
160+
function throttle(func, limit) {
161+
let timeoutId;
162+
return function() {
163+
clearTimeout(timeoutId); // Clear previous
164+
timeoutId = setTimeout(() => func(), limit);
165+
};
166+
}
167+
```
168+
169+
### 3. Forgetting to Pass Arguments
170+
```js
171+
// ❌ Wrong: Arguments are lost
172+
function throttle(func, limit) {
173+
return function() {
174+
func(); // No arguments passed
175+
};
176+
}
177+
178+
// ✓ Correct: Collect and pass arguments
179+
function throttle(func, limit) {
180+
return function(...args) {
181+
func.apply(this, args); // Pass all arguments
182+
};
183+
}
184+
```
185+
186+
## Interview Tips
187+
188+
### Questions You Might Be Asked
189+
1. **"What's the difference between throttle and debounce?"**
190+
- Throttle: Regular intervals during activity
191+
- Debounce: Single execution after activity stops
192+
193+
2. **"When would you use throttle over debounce?"**
194+
- Use throttle for continuous updates (scroll, resize, mouse move)
195+
- Use debounce for completion-based actions (search, form validation)
196+
197+
3. **"How would you implement leading vs trailing edge?"**
198+
- Leading: Execute immediately, ignore subsequent calls
199+
- Trailing: Schedule execution for end of interval
200+
- Both: Execute immediately and schedule final call
201+
202+
4. **"What are the performance benefits?"**
203+
- Reduces function calls (e.g., from 1000/sec to 10/sec)
204+
- Prevents browser lag and jank
205+
- Reduces API calls and server load
206+
207+
5. **"How do you preserve the `this` context?"**
208+
- Use regular function (not arrow function)
209+
- Use `func.apply(this, args)` to call original function
210+
211+
### Code Review Points
212+
- ✓ Preserves `this` context with `apply()`
213+
- ✓ Passes all arguments with rest/spread operators
214+
- ✓ Clears previous timeouts to prevent memory leaks
215+
- ✓ Uses closures correctly to maintain state
216+
- ✓ Handles edge cases (first call, rapid calls, etc.)
217+
218+
## Advanced Variations
219+
220+
### Throttle with Options
221+
```js
222+
function throttle(func, limit, options = {}) {
223+
const { leading = true, trailing = true } = options;
224+
let lastExecuted = 0;
225+
let timeoutId = null;
226+
227+
return function(...args) {
228+
const now = Date.now();
229+
230+
if (!lastExecuted && !leading) {
231+
lastExecuted = now;
232+
}
233+
234+
const remaining = limit - (now - lastExecuted);
235+
236+
if (remaining <= 0) {
237+
if (timeoutId) {
238+
clearTimeout(timeoutId);
239+
timeoutId = null;
240+
}
241+
lastExecuted = now;
242+
func.apply(this, args);
243+
} else if (!timeoutId && trailing) {
244+
timeoutId = setTimeout(() => {
245+
lastExecuted = leading ? Date.now() : 0;
246+
timeoutId = null;
247+
func.apply(this, args);
248+
}, remaining);
249+
}
250+
};
251+
}
252+
```
253+
254+
### Throttle with Cancel Method
255+
```js
256+
function throttle(func, limit) {
257+
let timeoutId = null;
258+
let lastExecuted = 0;
259+
260+
const throttled = function(...args) {
261+
const now = Date.now();
262+
const remaining = limit - (now - lastExecuted);
263+
264+
if (remaining <= 0) {
265+
lastExecuted = now;
266+
func.apply(this, args);
267+
}
268+
};
269+
270+
// Add cancel method
271+
throttled.cancel = function() {
272+
clearTimeout(timeoutId);
273+
timeoutId = null;
274+
lastExecuted = 0;
275+
};
276+
277+
return throttled;
278+
}
279+
```
280+
281+
## Testing Considerations
282+
```js
283+
// Test basic throttling
284+
const mockFn = jest.fn();
285+
const throttled = throttle(mockFn, 1000);
286+
287+
throttled(); // Should execute
288+
throttled(); // Should be ignored
289+
jest.advanceTimersByTime(1000);
290+
throttled(); // Should execute
291+
292+
expect(mockFn).toHaveBeenCalledTimes(2);
293+
```
294+
295+
## Related Patterns
296+
- **Debounce**: Delays execution until activity stops
297+
- **Rate Limiting**: Restricts number of calls in a time window
298+
- **Request Animation Frame**: Browser-optimized throttling for animations
299+
- **Web Workers**: Offload heavy computations to background threads
300+
301+
## Further Reading
302+
- [MDN: Closures](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures)
303+
- [Lodash throttle implementation](https://lodash.com/docs/#throttle)
304+
- [CSS Tricks: Debouncing and Throttling](https://css-tricks.com/debouncing-throttling-explained-examples/)

0 commit comments

Comments
 (0)