karawaci.kode

← Semua snippet

JavaScript Menengah Utility

Throttle function (companion ke debounce)

Throttle execute max 1x per interval. Berbeda dengan debounce yang tunggu pause. Pakai untuk scroll, mousemove, resize.

Dipublikasikan 1 Juni 2026

Saya pernah publish snippet debounce. Ini companion: throttle.

Beda:

  • Debounce: tunggu user berhenti, baru execute (search box).
  • Throttle: execute max 1x per interval (scroll progress bar).

Kode

/**
 * Throttle function — execute max 1x per interval.
 * @template {(...args: any[]) => any} F
 * @param {F} fn - Function to throttle.
 * @param {number} wait - Interval (ms).
 * @param {object} [options]
 * @param {boolean} [options.trailing=true] - Trigger trailing call after last invoke.
 * @returns {F & { cancel: () => void }}
 */
function throttle(fn, wait, options = {}) {
  let lastCall = 0;
  let timer = null;
  let lastArgs = null;
  let lastThis = null;
  const { trailing = true } = options;

  const throttled = function (...args) {
    const now = Date.now();
    const remaining = wait - (now - lastCall);
    lastArgs = args;
    lastThis = this;

    if (remaining <= 0 || remaining > wait) {
      // First call atau lebih dari wait sejak last call
      if (timer) {
        clearTimeout(timer);
        timer = null;
      }
      lastCall = now;
      fn.apply(this, args);
    } else if (!timer && trailing) {
      // Schedule trailing call
      timer = setTimeout(() => {
        lastCall = Date.now();
        timer = null;
        if (lastArgs) {
          fn.apply(lastThis, lastArgs);
          lastArgs = null;
          lastThis = null;
        }
      }, remaining);
    }
  };

  throttled.cancel = () => {
    if (timer) clearTimeout(timer);
    timer = null;
    lastCall = 0;
    lastArgs = null;
  };

  return throttled;
}

Pemakaian

// Update progress bar saat scroll
const updateProgress = throttle(() => {
  const scrollPct = window.scrollY / (document.body.scrollHeight - window.innerHeight);
  document.getElementById('progress').style.width = `${scrollPct * 100}%`;
}, 16); // 60fps cap

window.addEventListener('scroll', updateProgress, { passive: true });

// Tracking analytics scroll depth (max 1x per 5 detik)
const trackScrollDepth = throttle(() => {
  analytics.track('scroll_depth', { pct: Math.round(scrollPct * 100) });
}, 5000);

window.addEventListener('scroll', trackScrollDepth, { passive: true });

// Resize handler
const onResize = throttle(() => {
  recalculateLayout();
}, 200);

window.addEventListener('resize', onResize);

Throttle vs Debounce: cara pilih

SkenarioUse
Live search queryDebounce 300ms
Scroll progress barThrottle 16ms (60fps)
Scroll-based lazy loadThrottle 200ms
Auto-save documentDebounce 2000ms
Window resize handlerThrottle 100ms
Mouse move trackingThrottle 16ms
Form validation (live)Debounce 500ms
Track scroll depth (analytics)Throttle 5000ms

Catatan

  • { passive: true } pada addEventListener untuk scroll/touch events — kasih browser permission optimize.
  • Throttle wait=16 = 60fps. Untuk smooth animation, ini batas atas. Pertimbangkan requestAnimationFrame untuk visual animation.
  • Pakai cancel() di React useEffect cleanup atau Vue onBeforeUnmount untuk avoid memory leak.

Lodash punya _.throttle yang sudah matang. Snippet ini untuk yang gak mau dep tambahan.

# tags

throttleperformanceeventsvanilla

Ditulis oleh Asti Larasati · 1 Juni 2026