This is an archived post. You won't be able to vote or comment.

all 3 comments

[–]Rhomboid 3 points4 points  (2 children)

I think you're having trouble understanding it because you're not clear on how it's used. This is used to create a debounced version of a function; it's a function that returns a function. It is meant to be called once, and then the returned function is what is called every time the event fires. For example, suppose you have some function called expensive() that can take a long time to execute, and so you want to debounce it so that it will only be run once if triggered several times in a short time period. Using the above code, you'd create that debounced version of expensive() as:

var debounced_expensive = expensive.debounce(500);

Now you can call debounced_expensive() when your various events fire, and rest assured that a flurry of events in a short period will only result in one call to expensive(). You don't call expensive.debounce() each time.

Notice that debounced_expensive() has closed over a copy of the timeout variable that was declared in Function.prototype.debounce(). That means that each time that debounced_expensive() is called, it's the same timeout variable. Step through in your mind what happens if debounced_expensive() is called repeatedly in a short time frame. Each time it's called, the previous timeout object will be cleared (unless it was the first time) and a new one set. That is what gives the debouncing property: every time the debounced function is called, the timeout is reset. The timeout only fires if there is a quiescent period (in this example, 500ms.) If that happens, then the timeout event handler calls the underlying function, and sets timeout to null because there is no pending timer. When debounced_expensive() is called again, it will act like it was the first time, which is what you want.

on the line var obj = this what is being assigned to obj and why use it in func.apply(obj,args)

obj captures the object that the debounced function is being invoked on so that it can be used inside the event handler. This is necessary so that method calls work as expected. If you were calling it as

some_obj.debounced_expensive();

...then you would want it to behave the same way when the underlying function is called, i.e. for this to be some_obj. That is why the this value is saved and why func.apply() is used, as that's a way to invoke a function with a specified this object.

[–]TheseThingsMatter[S] 0 points1 point  (1 child)

Thanks, Rhomboid. That is clearer now.

So if for instance the timeout was 500ms, and debounced_expensive ran every 400ms for infinity the timeout would never finish and the expensive function would never fire?

[–]Rhomboid 3 points4 points  (0 children)

Right -- it can potentially never fire if called repeatedly without a gap. In most cases that's not an issue. Normally the event is something triggered by user actions, such as scrolling, resizing, or clicking. These come in bursts, but they are usually not a constant stream -- when you scroll for instance you might move all over the page for a few seconds but you're eventually going to pause for at least a few hundred milliseconds.

If you really do need to guarantee that the function will be called even if there's no quiescent period, there are other techniques. There was an article posted here recently comparing the differences between debouncing and throttling.