Better event throttle

Eckehard - Feb 26 - - Dev Community

Sometime you need to make your code slower to make it faster...

Usually we want our code execute as fast as possible. But in case of DOM events, it can be useful to limit execution rate (see this post). Some events like window scroll may fire with a very high frequency, which may lead to a poor user experience or even blocking of the event chain.

You will find numerous implementations of throttle functions, the most common is presented here. Usually throttle executes the first event and then limits the rate of repetition. But in a UI it may happen, that there is not a single event causing a state change, but a burst of events happening in a short time. You might have some coupled states that depend on each other. Firing the first event would propagate only the first state change, but the depending changes would be suppressed until the next update. To prevent this, you would use a "debounce" function, that waits until the state has settled before propagating the first event.

As the situation happens frequently, I found it helpful to combine both functions in an advanced throttle function:

function throttle(cb, delay = 10, rate = 50) {
  let wait = false
  let args, timeout
  const tf = () => {
    if (args == null) {
      wait = false
      clearTimeout(timeout)
    } else {
      cb(...args)
      args = null
      timeout = setTimeout(tf, rate)
    }
  }

  return (...arg) => {
    if (wait) {
      args = arg
      return
    }
    wait = true
    setTimeout(() => {
      cb(...arg)
      timeout = setTimeout(tf, rate)
    }, delay)

  }
}
Enter fullscreen mode Exit fullscreen mode

This version uses an initial delay before calling the event function. After that, it limits the throttle rate to a different value. After the last event has happened, the execution stops and the next event will be propagated after the initial delay again. The default values of 10 ms and 50 ms have been found the be nearly unnoticeable, but any other value may serve better in your case.

The throttle function can be used as a function wrapper, arguments provided to the result will be forwarded to the wrapped function (see example below)

  function showScroll(){
    out.textContent = "scroll-position = "+window.scrollY
  }
  window.onscroll =  throttle(showScroll,10,250)

  // function wrapper example
  function log(s){
     console.log(s)
  }
  const tlog = throttle(log,500,2000)
  tlog("This is a test")
Enter fullscreen mode Exit fullscreen mode

You can try out a working example here

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .