Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions coding-exercise/debounce-function/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Debounce Function

## Challenge
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.

## Problem Description
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.

### Real-World Use Cases
- **Search Input**: Wait for the user to stop typing before making an API call
- **Window Resize**: Wait for resize to finish before recalculating layout
- **Scroll Events**: Reduce the number of scroll event handlers fired
- **Button Clicks**: Prevent multiple form submissions

## Example

### Input
```js
function handleSearch(query) {
console.log(`Searching for: ${query}`);
}

const debouncedSearch = debounce(handleSearch, 500);

// User types rapidly
debouncedSearch('J');
debouncedSearch('Ja');
debouncedSearch('Jav');
debouncedSearch('JavaScript');
```

### Output
```
// Only executes once after 500ms of the last call
Searching for: JavaScript
```

## Requirements
1. The debounce function should accept a function and a delay time
2. It should return a new function that delays invoking the original function
3. Each new call should reset the delay timer
4. Only the last call should execute after the delay period
5. The function should preserve the correct `this` context and arguments

## Key Concepts
- **Closures**: Maintaining state (timeoutId) across function calls
- **Higher-Order Functions**: Returning a function from a function
- **setTimeout/clearTimeout**: Managing asynchronous delays
- **Function Context**: Using `apply()` to preserve `this` binding

## Difference from Throttling
- **Debounce**: Executes the function only after the calls have stopped for a specified period
- **Throttle**: Executes the function at most once per specified time interval

## Benefits
- Improves performance by reducing unnecessary function calls
- Reduces API calls and server load
- Provides better user experience by preventing excessive updates
114 changes: 114 additions & 0 deletions coding-exercise/debounce-function/debounce.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/**
* Creates a debounced version of a function that delays its execution
* until after a specified delay has elapsed since the last time it was invoked.
*
* @param {Function} func - The function to debounce
* @param {number} delay - The delay in milliseconds to wait before executing the function
* @returns {Function} A debounced version of the original function
*
* @example
* const debouncedFn = debounce(() => console.log('Hello'), 300);
* debouncedFn(); // Won't execute immediately
* debouncedFn(); // Resets the timer
* debouncedFn(); // Only this call will execute after 300ms
*/
function debounce(func, delay) {
// Store the timeout ID in closure scope
// This variable persists across multiple calls to the returned function
let timeoutId;

// Return a new function that wraps the original function
// ...args collects all arguments passed to this function
return function(...args) {
// Clear the previous timeout if it exists
// This prevents the previous scheduled execution from running
// Each new call "resets the timer"
clearTimeout(timeoutId);

// Schedule a new timeout to execute the function after the delay
// This creates a new timer that will execute after 'delay' milliseconds
timeoutId = setTimeout(() => {
// Execute the original function with the correct context and arguments
// func.apply(this, args) ensures:
// 1. 'this' context is preserved (important for object methods)
// 2. All arguments are passed to the original function
func.apply(this, args);
}, delay);
};
}

// ============================================
// EXAMPLE USAGE: Search Input Handler
// ============================================

/**
* Simulates a search API call handler
* In a real application, this would make an HTTP request to a search API
*
* @param {string} query - The search query string
*/
function handleSearch(query) {
console.log(`Searching for: ${query}`);
// In production, this would be something like:
// fetch(`/api/search?q=${query}`).then(...)
}

// Create a debounced version of handleSearch with 500ms delay
// This means the actual search will only execute 500ms after the user stops typing
const debouncedSearch = debounce(handleSearch, 500);

// ============================================
// DEMONSTRATION: Simulating Rapid User Input
// ============================================

console.log('User types rapidly...');

// Each call below happens almost instantly (simulating fast typing)
// But only the LAST call will actually execute after 500ms
debouncedSearch('J'); // Timer starts, will be cancelled
debouncedSearch('Ja'); // Previous timer cancelled, new timer starts
debouncedSearch('Jav'); // Previous timer cancelled, new timer starts
debouncedSearch('Java'); // Previous timer cancelled, new timer starts
debouncedSearch('JavaS'); // Previous timer cancelled, new timer starts
debouncedSearch('JavaSc'); // Previous timer cancelled, new timer starts
debouncedSearch('JavaScr'); // Previous timer cancelled, new timer starts
debouncedSearch('JavaScript'); // Previous timer cancelled, final timer starts

console.log('Waiting 500ms...');
// Only the last call will execute after 500ms delay
// Expected output after 500ms: "Searching for: JavaScript"
//
// Without debouncing, handleSearch would have been called 8 times!
// With debouncing, it's called only once - saving 7 unnecessary API calls

/**
* Explanation:
*
* This demonstrates the debounce pattern in JavaScript.
*
* 1. Debouncing delays the execution of a function until after a certain
* amount of time has passed since it was last called.
*
* 2. When debouncedSearch is called multiple times in quick succession,
* only the LAST call will execute after the delay period.
*
* 3. Each new call clears the previous timeout and sets a new one.
*
* 4. Common use cases:
* - Search input: Wait for user to stop typing before making API call
* - Window resize: Wait for resize to finish before recalculating layout
* - Scroll events: Reduce the number of scroll event handlers fired
*
* 5. Benefits:
* - Reduces unnecessary function calls
* - Improves performance
* - Reduces API calls and server load
*
* 6. The function uses closures to maintain the timeoutId across calls.
*
* 7. func.apply(this, args) ensures the original function is called with
* the correct context and arguments.
*
* Note: This is different from throttling, which executes the function
* at regular intervals regardless of how many times it's called.
*/