Tanmay Kachroo

Throttle and Debounce (in Typescript)


Throttle

Throttling ensures that the function is called at a regular interval, suppressing excessive calls and reducing the overall number of function invocations.

Example: In a scroll event handler, you might want to update the UI or trigger certain actions at a controlled rate to prevent excessive updates and improve performance.

type ThrottledFunction<T extends (...args: any[]) => any> = {
  (this: ThisParameterType<T>, ...args: Parameters<T>): void;
  cancel: () => void;
};

function throttle<T extends (...args: any[]) => any>(
  func: T,
  delay: number
): ThrottledFunction<T> {
  let lastCall = 0;
  let timeout: NodeJS.Timeout | null = null;

  const throttledFunc: ThrottledFunction<T> = function (
    this: ThisParameterType<T>,
    ...args: Parameters<T>
  ) {
    const now = Date.now();

    if (now - lastCall >= delay) {
      func.apply(this, args);
      lastCall = now;
    } else {
      if (timeout) {
        clearTimeout(timeout);
      }

      timeout = setTimeout(() => {
        func.apply(this, args);
        lastCall = Date.now();
        timeout = null;
      }, delay - (now - lastCall));
    }
  };

  throttledFunc.cancel = function () {
    if (timeout) {
      clearTimeout(timeout);
      timeout = null;
    }
  };

  return throttledFunc;
}

Example usage:

function handleScroll(event: Event) {
  console.log('Scrolled');
}

const throttledScroll = throttle(handleScroll, 100);

window.addEventListener('scroll', throttledScroll);

// To cancel the throttled function:
// throttledScroll.cancel();

Debounce

Debouncing is a technique where a function call is delayed until a certain amount of time has passed since the last invocation. If the function is called again within that time period, the timer is reset, and the function call is postponed again.

Example: In an autocomplete search field, you may want to wait for the user to finish typing before making a request to fetch suggestions.

function debounce<T extends (...args: any[]) => void>(
  func: T,
  delay: number
): (...args: Parameters<T>) => void {
  let timeoutId: NodeJS.Timeout;

  return (...args: Parameters<T>) => {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => {
      func(...args);
    }, delay);
  };
}

Example usage:

function search(query: string) {
  // Perform search operation
  console.log(`Searching for "${query}"...`);
}

const debouncedSearch = debounce(search, 300);

// Call the debounced function
debouncedSearch("keyword");
debouncedSearch("another keyword");
debouncedSearch("third keyword");

Reference: