Simplified debouncing - javascript.
With the web becoming more and more of a real-time platform, the need to give users an experience of immediate satisfaction has grown exponentially.
The journey of trying fulfill these demands has given us developers an array of challenges from performance, asynchronous and state management issues. While this ProTip won't cover the breadth of these challenges, it will cover async optimization through debouncing!
Debouncing is the practice of reducing HTTP overhead when working with immediate interactions.
The most common scenario is updating a record with data from a web-form without the use of a submit button. You can imagine, depending on the scope of the input field and the typing speed of the user, the amount of HTTP requests as a result of this can be substantial. Imagine if your site had thousands of concurrent users all filling out this form. You're basically DDoS'ing your own servers!
Searching the web will give you various debouncing systems, all with a slightly different flavor. While I think that is actually a really cool part about the JS community, I will talk about one of the methods that I find more efficient and easier to implement: the stop watch.
The stop watch method can be thought of as follows:
- Put a minute on the clock.
- Designate a specific action that when triggered, resets the clock.
- Designate a specific action that gets triggered when the clock runs out.
- Start the clock.
- Reset the clock to a stopped, yet ready state when it runs out.
In our case, one minute is entirely too long to expect a user to wait for a record to update or something of the sort.
We'll set it to 100 milliseconds.
Here’s what the above would look like in code.
// Let’s create a namespace for our time related functions.
var Timer = {};
// Debounce Interval, in ms. It’s a good idea to make something like this “implementer configurable”. This is the equivalent of “putting a minute on the clock".
Timer.debounceInterval = 100;
// Time value that gets manipulated
Timer.currentTime;
// Store the setInterval function
Timer.interval;
// Create a pointer to a function that gets called when the timer runs out.
// Be sure to only point to a reference, and not () an execution.
Timer.onTimeout = foo;
// This is the equivalent of running the clock and would be the callback of your registered event handler.
// The 'data' argument more or less represents an event object from the aforementioned handler.
Timer.run = function(data) {
var self = this;
// Set/reset time.
this.currentTime = this.debounceInterval;
// Prevent the current interval from running out - this isn’t technically necessary with the current code, but it helps me feel like I’m covering a potential gap.
clearInterval(self.interval);
// Create a new one.
// We use setInterval here to get as close to a 1ms representation as possible.
this.interval = setInterval(function() {
// Decrement time
self.currentTime--;
// Check for timeout. Since zero is falsy we can get away with just doing this:
if(!self.currentTime) {
// Run the callback
self.onTimeout(data);
// If it timesout, clear the interval
clearInterval(self.interval);
}
}, 1);
};
That’s really as complex as it gets!
For an implementation example, see this fiddle: http://jsfiddle.net/ktstowell/6cmLG/