javascript - choosing the right timeout is easy

javascript - choosing the right timeout is easy

Every once in awhile I see code that debounces expensive events like resize or scroll:

someTimeout = window.setTimeout(debounceScroll, 10);

and I wonder about the magic numbers, developers choose.

magic numbers

In the example the dev thinks 10 milliseconds is a good time to debounce this event. The whole code may looks like this:

function handleScroll(event) {
  if (someTimeout) {
    window.clearTimeout(someTimeout);
  }
  someTimeout = window.setTimeout(debounceScroll, 10);
}
...
window.addEventListener('scroll', handleScroll);

If the scroll event fires again within 10 ms, it is not processed.

a little excourse

Magic numbers always make me think. Let's take a quick look into SCSS:

@function magic-number-in-use($anyValue) {
  @return $anyValue + 68px;
}

The 68px seem to be a global need but why and where do they come from. If you use raw values in a function, in javascript or scss, add a variable or constant for it. Choosing a good name for that value, will help every other developer.

@function no-more-magic-number($anyValue) {
  @return $anyValue + $headerHeight; 
}

back on track

Every time you use a magic number in javascript you may be wrong. The browser will tell you when he is ready for your code. With requestAnimationFrame or requestIdleCallback you will always hit a good moment to execute your piece of code.

With that in mind, the code above looks like this:

if (scrollTimeout) {
  window.cancelAnimationFrame(scrollTimeout);
}
scrollTimeout = window.requestAnimationFrame(debounceScroll);

A requestAnimationFrame callback is executed after a browser painted the page on your display. If your code executes faster than 16ms, your user will not feel any interruption. By using a magic number setTimeout, your code may be called within the last millisecond of the browsers rendering window, and your UX will feel janky.

If your code is not that critical, you can use requestIdleCallback, in that moment, the browser is not doing anything - but today (04/Oct/2017) browser support is not that good: caniuse#requestIdleCallback.

In Short

Usage of magic numbers is never a good idea. It confuses other developers or yourself, if you are reviewing your code after a month.

In javascript always check if a browser API may help you. If you need a 300 millisecond timeout, that is fine, just let your code be executed in a requestAnimationFrame callback:

function funcA() {
  // do something
}
function timeoutCallback() {
  window.requestAnimationFrame(funcA);
}
window.setTimeout(timeoutCallback, 300);

No one will feel the difference between 300 and 316 ms - but every user will notice if the browser cannot render a frame every 1/60 of a second.

header code

For the header image, I wrote a small scroll listener function, using a setTimeout, requestAnimationFrame and requestIdleCallback to demonstrate their behavior: