Function Debouncing with Underscore.js

By  on  

The ability to listen and react to user interactions with JavaScript is fundamental and incredibly useful. Some interactions happen frequently and some rarely. Some listener functions are light of action, others can be quite taxing on the browser. Take window's resize event for example: the event fires at each step in the resize, so if you have a taxing event listener, your user's browser will get bogged down quickly.

Obviously we can't allow the user's browser to get bogged down, but we can't simply remove listener function either. What we can do, however, is use debouncing to temper the amount of time the method runs. Instead of the listener function firing on each iteration of the resize event, we can ensure it fires only every n milliseconds during the resize, allowing our functionality to fire but at a rate so as to not ruin the user's experience. A great utility called Underscore.js provides an easy to use method for easily creating debouncing event listener functions.

The JavaScript

Creating a debouncing event listener is as easy as:

// Create the listener function
var updateLayout = _.debounce(function(e) {

	// Does all the layout updating here
}, 500); // Maximum run of once per 500 milliseconds

// Add the event listener
window.addEventListener("resize", updateLayout, false);

..because the Underscore.js code underneath the hood manages the interval checks and original listener function calling:

// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
_.debounce = function(func, wait, immediate) {
	var timeout;
	return function() {
		var context = this, args = arguments;
		var later = function() {
			timeout = null;
			if (!immediate) func.apply(context, args);
		var callNow = immediate && !timeout;
		timeout = setTimeout(later, wait);
		if (callNow) func.apply(context, args);

Not the most complex piece of code but nice that you don't have to write it yourself. The debounce method doesn't rely on any other Underscore.js methods, so you can add this method to a framework like jQuery or MooTools quite easily:

// MooTools
	debounce: function(wait, immediate) {
		var timeout, 
		    func = this;
		return function() {
			var context = this, args = arguments;
			var later = function() {
				timeout = null;
				if (!immediate) func.apply(context, args);
			var callNow = immediate && !timeout;
			timeout = setTimeout(later, wait);
			if (callNow) func.apply(context, args);

// Use it!
window.addEvent("resize", myFn.debounce(500));

As mentioned above, window resize events are the most obvious place to use debouncing, but you could also use them for key events that trigger an autocompleter. I love tiny pieces of code like this that can enhance a site's efficiency so quickly! I also recommend you take a look at Underscore.js and the numerous utility functions it provides -- enrich your existing framework or use it as is!

Recent Features

  • By
    Chris Coyier’s Favorite CodePen Demos

    David asked me if I'd be up for a guest post picking out some of my favorite Pens from CodePen. A daunting task! There are so many! I managed to pick a few though that have blown me away over the past few months. If you...

  • By
    Welcome to My New Office

    My first professional web development was at a small print shop where I sat in a windowless cubical all day. I suffered that boxed in environment for almost five years before I was able to find a remote job where I worked from home. The first...

Incredible Demos

  • By
    Form Element AJAX Spinner Attachment Using MooTools

    Many times you'll see a form dynamically change available values based on the value of a form field. For example, a "State" field will change based on which Country a user selects. What annoys me about these forms is that they'll often do an...

  • By
    Full Width Textareas

    Working with textarea widths can be painful if you want the textarea to span 100% width.  Why painful?  Because if the textarea's containing element has padding, your "width:100%" textarea will likely stretch outside of the parent container -- a frustrating prospect to say the least.  Luckily...


  1. Aicke Schulz

    I want to mention, that there are already two similar solutions for MooTools, realized as “pseudo events”:

  2. Rolf

    Yes yes, exactly why I ripped that method out of _.js code and put it inside my Moo code for window resize events :) – good tip to share!

  3. piotr

    In case you are already using Mootools in the project, consider Events.Pseudos :once, :throttle and :pause (

    window.addEvent('resize:throttle(400)', function(){
        // Will only fire once every 400 ms
  4. Alex

    You always post things just in time. THANKS!

  5. good one! i think it’s the same with Ben Alman’s debounce which is a jQuery plugin

  6. This is really useful. very snap and play which I like. I’m interested to see if this will work on a mobile device and what listeners other listeners I could use. Thanks for the post.

  7. Thanks, really interesting.

  8. Albert

    Great, just what I needed. Can’t use MooTools event.pseudos because I’m bounded to version 1.2.5

  9. Alfonso Perez

    This is the nth-time I benefit from one of your posts, so I have decided to take some seconds to say: thanks!,

    btw, as my humble way of saying thanks, I have paused for a moment adblock and show some genuine interest for your sidebar ad ;-).

  10. Volkan

    Why they used the code like that?

    func.apply(context, args);

    I can change it with func() and it executes as is.

  11. Guy

    I know this is an old post but it’s still coming up as the second hit for the google search ‘underscore debounce’ so I thought I’d chip in.

    The function you’re describing in the 2nd paragraph – “…we can ensure it fires only every n milliseconds during the resize..” seems to actually be _throttle.
    _debounce only calls the given function once, an amount of time only after it has stopped being continually fired.


  12. Dick

    Damn confusing code, your documentation skills and variable naming needs improving. There’s simpler code to do this on the net too.

    • NotHim

      @Dick As stated in the article itself, this code is part of the Underscore.js library, not his code. If you have issues with the documentation and variable naming, bring it up to the Underscore.js developers.

  13. S

    So immediate only applies to the first click? I was assuming that it made all clicks immediate, so i was really confused why the timeout variable was being compared in callnow.

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!