Managing JavaScript events/functions using "debouncing"

Yesterday I came across a very timely post by John Hann on Debouncing Javascript Methods. I say timing, because this was a problem I was just getting ready to solve again for umpteenth time—managing rapidly fired events in JavaScript.

I was working on some live search functionality for a page where I was searching DOM elements and populating a list of matching elements. Since this was a "live" search, I'm doing the update as the user types. The trick to this issue is that you don't really want to fire off the process each time the user presses a key, but instead you want to really fire it off when there's been a delay/pause to their typing. If you actually do the processing on each character, many of the calls you're making become quickly invalidating as the input changes so quickly that you end up making unnecessary calls. You'll also see a noticeable slow down in performance if your process is CPU intensive.

You can run into the same issue when dealing with AJAX operations. If you're just updating some portion of the screen based upon some user interaction, you really only want to fire off the AJAX call when the user is done interacting with the element.

This is where John's debounce solution comes into play.

In the past, I've always rolled up some solution manually and never came up with a particularly good re-usable solution (which John's has done with the debounce functions.) The idea John's using is essentially the same one I've used—fire off an async event using setTimeout() with a short delay which is cancelled if there's another request to the same function within the delay. What this does is make sure we only run the function when it really matters. This means if we fire off a function 10 times in 1 second, we only ever end up triggering the logic the last time we call the function (because the first 9 iterations become outdated so quickly, there's no point in run the process.)

What John has done is come up with a re-usable function that handle all the heavy lifting for you. No longer do you need to worry about handling this logic within your function, the debounce() function handles it for you.

While the function itself isn't very big, the syntax may look confusing if you're not well versed in JavaScript. In a nutshell, what happens is your original function is replaced by a copy of the function that is debounced. So, if the function is called multiples times within the threshold value, then only the calls that occur outside of that threshold are actually processed.

Since this concept is easier grasps seeing a live example, let's take a look at one.

In the following example, we will monitor each key press and output the value. Here's the source code we're using:

The only key difference is that we've added the debounce() function after the end of the declaration of the function. This works because debounce() was defined as a method of the Function object. The argument value of 250 is telling the debounce function to only run the function if 250ms has passed since the last time the function was called.

Now try typing in the box below. You'll notice that updates only appear when there's a pause in your typing. If you are typing more than 1 character a second, then the updates will not appear until you stop typing.

Type:

NOTE:

While I've used 500ms for the debounce threshold, in my testing 250ms appears to be a better value for keyboard related events.

As you can see, the behavior in the debounce() version offers behavior that much more conducive to what we need—especially if you're dealing with AJAX-based operation. If you're firing off the event after each keystroke, you're just performing operations that are invalidated almost immediately and will only slow down performance of your application.

The debouce technique can really be used for lots of things besides just monitoring keystrokes on an element—you could use it to monitor mouse movement or to even handle mouse clicks. It comes in handy any time you really only want to trigger an operation once activity has settled down.

Dan, this rocks! I was also just thinking about how to roll my own debounce-like functionality (didn't know the technique actually had a name) for a project I'm working on, but this is so much better! Thanks for posting this entry!

If we're looking solely at keyboard entry, then if you're interacting with a series of characters (such as the user typing in a text field) you're generally going to want to process when the typing stops (which is the default behavior.)

However, maybe you're monitoring mouse clicks so you want to trigger something on the first click and essentially "ignore" the additional clicks--that's when running it ASAP would be handy.

You could definitely simplify the code for specific use cases, but John Hahn tried to develop a re-usable piece he could use for many use cases.