Skip to content

Commit 9ff38fe

Browse files
authored
Merge pull request #323 from codomposer/new/debounce-function
add debounce function guide for coding exercise
2 parents 5cf65a7 + 519f793 commit 9ff38fe

File tree

2 files changed

+172
-0
lines changed

2 files changed

+172
-0
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Debounce Function
2+
3+
## Challenge
4+
Implement a debounce function that delays the execution of a callback until after a specified delay period has elapsed since the last time it was invoked.
5+
6+
## Problem Description
7+
Debouncing is a programming practice used to ensure that time-consuming tasks do not fire so often. It limits the rate at which a function can fire.
8+
9+
### Real-World Use Cases
10+
- **Search Input**: Wait for the user to stop typing before making an API call
11+
- **Window Resize**: Wait for resize to finish before recalculating layout
12+
- **Scroll Events**: Reduce the number of scroll event handlers fired
13+
- **Button Clicks**: Prevent multiple form submissions
14+
15+
## Example
16+
17+
### Input
18+
```js
19+
function handleSearch(query) {
20+
console.log(`Searching for: ${query}`);
21+
}
22+
23+
const debouncedSearch = debounce(handleSearch, 500);
24+
25+
// User types rapidly
26+
debouncedSearch('J');
27+
debouncedSearch('Ja');
28+
debouncedSearch('Jav');
29+
debouncedSearch('JavaScript');
30+
```
31+
32+
### Output
33+
```
34+
// Only executes once after 500ms of the last call
35+
Searching for: JavaScript
36+
```
37+
38+
## Requirements
39+
1. The debounce function should accept a function and a delay time
40+
2. It should return a new function that delays invoking the original function
41+
3. Each new call should reset the delay timer
42+
4. Only the last call should execute after the delay period
43+
5. The function should preserve the correct `this` context and arguments
44+
45+
## Key Concepts
46+
- **Closures**: Maintaining state (timeoutId) across function calls
47+
- **Higher-Order Functions**: Returning a function from a function
48+
- **setTimeout/clearTimeout**: Managing asynchronous delays
49+
- **Function Context**: Using `apply()` to preserve `this` binding
50+
51+
## Difference from Throttling
52+
- **Debounce**: Executes the function only after the calls have stopped for a specified period
53+
- **Throttle**: Executes the function at most once per specified time interval
54+
55+
## Benefits
56+
- Improves performance by reducing unnecessary function calls
57+
- Reduces API calls and server load
58+
- Provides better user experience by preventing excessive updates
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/**
2+
* Creates a debounced version of a function that delays its execution
3+
* until after a specified delay has elapsed since the last time it was invoked.
4+
*
5+
* @param {Function} func - The function to debounce
6+
* @param {number} delay - The delay in milliseconds to wait before executing the function
7+
* @returns {Function} A debounced version of the original function
8+
*
9+
* @example
10+
* const debouncedFn = debounce(() => console.log('Hello'), 300);
11+
* debouncedFn(); // Won't execute immediately
12+
* debouncedFn(); // Resets the timer
13+
* debouncedFn(); // Only this call will execute after 300ms
14+
*/
15+
function debounce(func, delay) {
16+
// Store the timeout ID in closure scope
17+
// This variable persists across multiple calls to the returned function
18+
let timeoutId;
19+
20+
// Return a new function that wraps the original function
21+
// ...args collects all arguments passed to this function
22+
return function(...args) {
23+
// Clear the previous timeout if it exists
24+
// This prevents the previous scheduled execution from running
25+
// Each new call "resets the timer"
26+
clearTimeout(timeoutId);
27+
28+
// Schedule a new timeout to execute the function after the delay
29+
// This creates a new timer that will execute after 'delay' milliseconds
30+
timeoutId = setTimeout(() => {
31+
// Execute the original function with the correct context and arguments
32+
// func.apply(this, args) ensures:
33+
// 1. 'this' context is preserved (important for object methods)
34+
// 2. All arguments are passed to the original function
35+
func.apply(this, args);
36+
}, delay);
37+
};
38+
}
39+
40+
// ============================================
41+
// EXAMPLE USAGE: Search Input Handler
42+
// ============================================
43+
44+
/**
45+
* Simulates a search API call handler
46+
* In a real application, this would make an HTTP request to a search API
47+
*
48+
* @param {string} query - The search query string
49+
*/
50+
function handleSearch(query) {
51+
console.log(`Searching for: ${query}`);
52+
// In production, this would be something like:
53+
// fetch(`/api/search?q=${query}`).then(...)
54+
}
55+
56+
// Create a debounced version of handleSearch with 500ms delay
57+
// This means the actual search will only execute 500ms after the user stops typing
58+
const debouncedSearch = debounce(handleSearch, 500);
59+
60+
// ============================================
61+
// DEMONSTRATION: Simulating Rapid User Input
62+
// ============================================
63+
64+
console.log('User types rapidly...');
65+
66+
// Each call below happens almost instantly (simulating fast typing)
67+
// But only the LAST call will actually execute after 500ms
68+
debouncedSearch('J'); // Timer starts, will be cancelled
69+
debouncedSearch('Ja'); // Previous timer cancelled, new timer starts
70+
debouncedSearch('Jav'); // Previous timer cancelled, new timer starts
71+
debouncedSearch('Java'); // Previous timer cancelled, new timer starts
72+
debouncedSearch('JavaS'); // Previous timer cancelled, new timer starts
73+
debouncedSearch('JavaSc'); // Previous timer cancelled, new timer starts
74+
debouncedSearch('JavaScr'); // Previous timer cancelled, new timer starts
75+
debouncedSearch('JavaScript'); // Previous timer cancelled, final timer starts
76+
77+
console.log('Waiting 500ms...');
78+
// Only the last call will execute after 500ms delay
79+
// Expected output after 500ms: "Searching for: JavaScript"
80+
//
81+
// Without debouncing, handleSearch would have been called 8 times!
82+
// With debouncing, it's called only once - saving 7 unnecessary API calls
83+
84+
/**
85+
* Explanation:
86+
*
87+
* This demonstrates the debounce pattern in JavaScript.
88+
*
89+
* 1. Debouncing delays the execution of a function until after a certain
90+
* amount of time has passed since it was last called.
91+
*
92+
* 2. When debouncedSearch is called multiple times in quick succession,
93+
* only the LAST call will execute after the delay period.
94+
*
95+
* 3. Each new call clears the previous timeout and sets a new one.
96+
*
97+
* 4. Common use cases:
98+
* - Search input: Wait for user to stop typing before making API call
99+
* - Window resize: Wait for resize to finish before recalculating layout
100+
* - Scroll events: Reduce the number of scroll event handlers fired
101+
*
102+
* 5. Benefits:
103+
* - Reduces unnecessary function calls
104+
* - Improves performance
105+
* - Reduces API calls and server load
106+
*
107+
* 6. The function uses closures to maintain the timeoutId across calls.
108+
*
109+
* 7. func.apply(this, args) ensures the original function is called with
110+
* the correct context and arguments.
111+
*
112+
* Note: This is different from throttling, which executes the function
113+
* at regular intervals regardless of how many times it's called.
114+
*/

0 commit comments

Comments
 (0)