Debounce your JavaScript functions

Written by on March 23rd, 2009 in Uncategorized.

John Hann has written an enjoyable post on debouncing JavaScript methods that comes with a fun back story on a project that John worked on.

John gets to the matter of debouncing:

Debouncing means to coalesce several temporally close signals into one signal. For example, your computer keyboard does this. Every time you hit a key, the contacts actually bounce a few times, causing several signals to be sent to the circuitry. The circuitry determines that the bouncing has ended when no bounces are detected within a certain period (the “detection period”). Since people can’t really type faster than roughly 10 keys per second, any signals happening within 100 msec of each other, for example, are likely part of the same key press. (In practice, you should at least halve this, so about 50 msec for our keyboard example. I have no idea what keyboards really use, by the way. This is just an illustration.)

Whenever I bring up the concept of debouncing, developers try to cast it as just a means of throttling. But that’s not true at all. Throttling is the reduction in rate of a repeating event. Throttling is good for reducing mousemove events to a lesser, manageable rate, for instance.

Debouncing is quite more precise. Debouncing ensures that exactly one signal is sent for an event that may be happening several times — or even several hundreds of times over an extended period. As long as the events are occurring fast enough to happen at least once in every detection period, the signal will not be sent!

Let’s relate this back to our keyboard-oriented user and our huge set of form fields. Throttling here would certainly help. We could reduce the number of XHR requests to a lower rate than the computer’s key repeat rate for sure! However, we’d still be fetching from the back-end more times than necessary, and the occasional re-rendering of the fetched data could temporarily freeze up the browser, deteriorating the user experience.

Debouncing on the other hand could better detect when the user stopped leaning on the keyboard and had arrived at their destination. It’s certainly not perfect. The user still may overshoot their destination, hesitate, and back-track, causing enough delay for the debounce detection period to expire. However, our tests showed that debouncing did a much better job of reducing XHR requests than throttling.

John then shows this in code:

JAVASCRIPT:

  1.  
  2. Function.prototype.debounce = function (threshold, execAsap) {
  3.     var func = this, // reference to original function
  4.         timeout; // handle to setTimeout async task (detection period)
  5.     // return the new debounced function which executes the original function only once
  6.     // until the detection period expires
  7.     return function debounced () {
  8.         var obj = this, // reference to original context object
  9.             args = arguments; // arguments at execution time
  10.         // this is the detection function. it will be executed if/when the threshold expires
  11.         function delayed () {
  12.             // if we’re executing at the end of the detection period
  13.             if (!execAsap)
  14.                 func.apply(obj, args); // execute now
  15.             // clear timeout handle
  16.             timeout = null;
  17.         };
  18.         // stop any current detection period
  19.         if (timeout)
  20.             clearTimeout(timeout);
  21.         // otherwise, if we’re not already waiting and we’re executing at the beginning of the waiting period
  22.         else if (execAsap)
  23.             func.apply(obj, args); // execute now
  24.         // reset the waiting period
  25.         timeout = setTimeout(delayed, threshold || 100);
  26.     };
  27. }
  28.  

There is also a version that doesn’t augment Function if that is more to your fancy.

Finally, some uses:

JAVASCRIPT:

  1.  
  2. // using debounce in a constructor or initialization function to debounce
  3. // focus events for a widget (onFocus is the original handler):
  4. this.debouncedOnFocus = this.onFocus.debounce(500, false);
  5. this.inputNode.addEventListener(‘focus’, this.debouncedOnFocus, false);
  6.  
  7. // to coordinate the debounce of a method for all objects of a certain class, do this:
  8. MyClass.prototype.someMethod = function () {
  9.     /* do something here, but only once */
  10. }.debounce(100, true); // execute at start and use a 100 msec detection period
  11.  
  12. // wait until the user is done moving the mouse, then execute
  13. // (using the stand-alone version)
  14. document.onmousemove = debounce(function (e) {
  15.     /* do something here, but only once after mouse cursor stops */
  16. }, 250, false);
  17.  

Source: Ajaxian » Front Page
Original Article: http://feedproxy.google.com/~r/ajaxian/~3/AFLFXp-WyNY/debounce-your-javascript-functions

Comments are closed.



Site Navigation