BRUCE_FEBRUCE_FE

EN/CH Mode

BRUCE_FE JS Interview Notes - User Interaction Optimization - Throttle and Debounce

In-depth understanding of Throttle and Debounce techniques in JavaScript, including their differences, implementation principles, applicable scenarios, and practical examples to master frontend performance optimization and common interview questions.

影片縮圖

Lazy to read articles? Then watch videos!

What are Throttle and Debounce? Why do we need them?

Throttle and Debounce are two common frontend performance optimization techniques used to control the execution frequency of high-frequency events (such as scrolling, searching, and window resizing).

  • Throttle: Limits a function to execute only once within a time period, regardless of how many times it's triggered. Like "execute at most once every N milliseconds".
  • Debounce: Merges multiple consecutive triggers into one, executing only after waiting a period of time after the last trigger. Like "wait N milliseconds without new triggers before executing".

These two techniques are very important for optimizing frontend performance and enhancing user experience:

  • Reduce unnecessary function calls
  • Decrease API request frequency
  • Optimize resource-intensive calculations
  • Reduce DOM operation frequency

Throttle Implementation and Principles

The core concept of throttling is 'ensuring that a function executes at most once within a specified time period':

function throttle(func, delay) {
  let lastCall = 0;
  
  return function(...args) {
    const now = Date.now();
    if (now - lastCall >= delay) {
      func.apply(this, args);
      lastCall = now;
    }
  };
}

// Usage example
const throttledScroll = throttle(() => {
  console.log('Scroll position:', window.scrollY);
}, 300);

window.addEventListener('scroll', throttledScroll);

Diagram of Throttle execution principle:

Event triggers: ↓  ↓  ↓  ↓  ↓  ↓  ↓  ↓  ↓
Function executes: ↓        ↓        ↓
Timeline:        |--------|--------|------->
                300ms    300ms    300ms

Debounce Implementation and Principles

The core concept of debouncing is 'waiting until there are no new calls within a specified time period before executing a function':

function debounce(func, delay) {
  let timer = null;
  
  return function(...args) {
    clearTimeout(timer);
    timer = setTimeout(() => {
      func.apply(this, args);
    }, delay);
  };
}

// Usage example
const searchInput = document.getElementById('search');
const debouncedSearch = debounce((e) => {
  console.log('Search keyword:', e.target.value);
  // fetchResults(e.target.value);
}, 500);

searchInput.addEventListener('input', debouncedSearch);

Diagram of Debounce execution principle:

Event triggers: ↓  ↓  ↓      ↓ ↓          ↓
Reset timer:   ⟳  ⟳  ⟳      ⟳ ⟳          ⟳
Function executes:           ✓            ✓
Timeline:      |-----|-----|-----|-----|---->
            Waiting Waiting Execute Waiting Execute

Use Case Comparison

Comparing Suitable Scenarios for Throttle and Debounce:

TechniqueSuitable ScenariosPractical Applications
ThrottleScroll Event HandlingInfinite scroll loading in social media, checking scroll position every 300ms
Game Button ControlsLimiting players to shooting at most 5 bullets per second in a shooting game
Map Dragging/ZoomingWhen dragging Google Maps, requesting map data only every 100ms
Canvas DrawingIn drawing applications, brush follows mouse to draw points at fixed intervals
DebounceSearch Box InputGoogle search box sends search request only 500ms after stopping typing
Form ValidationIn registration forms, checking if email is already in use only after the user finishes typing
Auto-SaveGoogle Docs auto-saves content 2 seconds after the user stops typing
Window ResizingResponsive websites reformat only after the user finishes resizing the window

Practical Applications and Performance Differences

Case 1: Scroll Event Optimization (Using Throttle)

Before optimization, scrolling might trigger hundreds of function calls:

// Unoptimized - Executes on every scroll
window.addEventListener('scroll', () => {
  loadMoreContent();
});

// Optimized - Executes at most once every 200ms
window.addEventListener('scroll', throttle(() => {
  loadMoreContent();
}, 200));

Case 2: Auto-saving Forms (Using Debounce)

Before optimization, typing each character would trigger a save:

// Unoptimized - Saves on every input
editor.addEventListener('input', saveContent);

// Optimized - Saves only 500ms after stopping input
editor.addEventListener('input', debounce(saveContent, 500));

🔥 Common Interview Questions

(1) What is the main difference between Throttle and Debounce?

Answer:Throttle controls a function to execute at most once within a time period, regardless of how many times it's triggered, suitable for continuous events like scrolling. For example: on an e-commerce website, when users scroll the page, using Throttle to check every 200ms whether more products need to be loaded, instead of triggering on every scroll.Debounce merges multiple consecutive triggers into one, executing only after waiting a period of time after the last trigger, suitable for scenarios that require only the final result of consecutive triggers, like searching. For example: in a search box, when a user quickly types "React tutorial", using Debounce to wait 500ms after the user stops typing before sending an API request, rather than sending a request for each keystroke.

(2) What is the execution result of the following code?

let count = 0;

const btn = document.getElementById('btn');
const throttledClick = throttle(() => {
  console.log(`點擊次數: ${++count}`);
}, 1000);

// 假設使用者在 2 秒內快速點擊 5 次
btn.addEventListener('click', throttledClick);
Answer:Assuming the user clicks 5 times within 2 seconds, the console will output:
  1. At 0 seconds: "Click count: 1" (executes immediately on first click)
  2. At 1 second: "Click count: 2" (executes after the 1-second interval)
  3. At 2 seconds: "Click count: 3" (executes after the 1-second interval)
It won't execute 5 times, but follows the throttle time interval, executing at most once per second.

(3) What is the execution result of the following debounce code?

let count = 0;

const searchInput = document.getElementById('search');
const debouncedSearch = debounce(() => {
  console.log(`搜尋次數: ${++count}`);
}, 500);

// 假設使用者在 1 秒內快速輸入 5 個字元
searchInput.addEventListener('input', debouncedSearch);
Answer:Assuming the user types 5 characters within 1 second, the console will output:
  1. At 1.5 seconds: "Search count: 1" (executes 500ms after the last input)
It won't execute 5 times, but following the debounce principle, it executes only once after waiting 500ms after the last input event. If the user types new characters before the timer ends, the timer restarts. This is why debounce is suitable for search boxes and other scenarios where you want to wait for the user to stop typing before executing an operation.

(4) Implement a simple search box Debounce

Answer:Here is a simple implementation of a search box with Debounce:

// HTML: <input id="search" type="text" placeholder="Search...">
// HTML: <div id="results"></div>

function debounce(func, delay) {
  let timer;
  return function(...args) {
    clearTimeout(timer);
    timer = setTimeout(() => func(...args), delay);
  };
}

const searchInput = document.getElementById('search');
const resultsDiv = document.getElementById('results');

const handleSearch = debounce((event) => {
  const query = event.target.value;
  resultsDiv.textContent = `Searching: ${query}`;
  // In a real application, an API request would be sent here
}, 300);

searchInput.addEventListener('input', handleSearch);