JavaScript by Example

Leading Debounce

Run the first trigger immediately, then ignore repeated triggers until the debounce window clears.

Normal debounce waits for silence before running. Leading debounce runs right away, then blocks repeats until the delay passes.

This is useful for buttons where the first click should respond immediately, but accidental double-clicks should not repeat the work.

function debounceImmediate(fn, delay = 300) {
  let timerId;
 
  return function debounced(...args) {
    const shouldRunNow = timerId === undefined;
 
    clearTimeout(timerId);
    timerId = setTimeout(() => {
      timerId = undefined;
    }, delay);
 
    if (shouldRunNow) {
      fn.apply(this, args);
    }
  };
}
 
const submitButton = document.querySelector("button[type='submit']");
 
submitButton.addEventListener(
  "click",
  debounceImmediate((event) => {
    event.preventDefault();
    console.log("submit once right away");
  }, 1000),
);

In production

Leading debounce is a guardrail, not security. Keep server-side idempotency for payments, uploads, and other actions where a double submission would be expensive.

Enjoyed this? Get more software craft delivered to your inbox.