It’s very cool to show the user search results in real time as they type things, but you don’t want to DDoS your server.

DDoS? Say you are making an online shop website, and the user types in Christmas midget cosplay costume, without debouncing, the requests will look like this:

“GET /search?q=c HTTP/1.1” 200

“GET /search?q=ch HTTP/1.1” 200

“GET /search?q=chr HTTP/1.1” 200

“GET /search?q=chri HTTP/1.1” 200

“GET /search?q=chris HTTP/1.1” 200

… (27 nearly simultaneous requests later)

“GET /search?q=christmas%20midget%20cosplay%20costume HTTP/1.1” 200

This creates a huge ton of traffic and might overwhelm the server if the searching is inherently taxing.

To debounce means we will wait for a certain time of inactivity before we send any request at all, so that no requests will be sent while the user is typing away, and we only send the request when the user stops.

How long we should wait is then a crucial question. Too long, and the user feels laggy, too short, and the debouncing is not effective. To do a decent job, we need to know how fast people type, and wait for a little longer than the time interval between key strokes.

To determining the wait time for debouncing, I studied a research conducted over 4,000 people in the 90s (download pdf here). The research recorded the distribution of people’s typing speed in terms of word per minute, or wpm. With some calculation involving the average frequent English word length, accounting for spaces between each word, I compile the following table:

  Time between keystrokes (ms)
The fastest 10% of people 97 to 164
2nd 10 percent 167 to 188
3rd 10 percent 191 to 215
4th 10 percent 219 to 239
5th 10 percent 245 to 277
6th 10 percent 284 to 310
7th 10 percent 319 to 351
8th 10 percent 363 to 405
9th 10 percent 421 to 501
The slowest 10 percent 526 to 2632

The data, for example, tells us 250 ms of inactivity would mean 50% of people have stopped typing.

The final waiting time could still be quite subjective. Setting it to 450 would mean roughly 90% of the invalid requests are prevented, and it seems quite good to me.

Code Example: Javascript

Here is some example debouncing javascript using 450 ms as the wait time. Packages with debouncing utilities should be plenty. But why not include this clean function:

/**
 * A debounce function. Documentation: https://davidwalsh.name/javascript-debounce-function
 * @param  {Function} func      The function to debounce
 * @param  {Number}   wait      The time to wait, in milliseconds
 * @param  {Boolean}  immediate Whether to invoke the function immediately
 * @return {Function}
 */
function debounce(func, wait, immediate) {

  let timeout

  return function debounced(...args) {

    const later = () => {
      timeout = null
      if (!immediate) func.apply(this, args)
    }

    const callNow = immediate && !timeout

    clearTimeout(timeout)
    timeout = setTimeout(later, wait)

    if (callNow) func.apply(this, args)

  }
}

searchBar.addEventListener('input', debounce(()=>{
    search()
}, 450))