Reputation: 3742
Hello I cannot seem to figure out why the debounce function works as expected when passed directly to a keyup event; but it does not work if I wrap it inside an anonymous function.
I have fiddle of the problem: http://jsfiddle.net/6hg95/1/
EDIT: Added all the things I tried.
HTML
<input id='anonFunction'/>
<input id='noReturnAnonFunction'/>
<input id='exeDebouncedFunc'/>
<input id='function'/>
<div id='output'></div>
JAVASCRIPT
$(document).ready(function(){
$('#anonFunction').on('keyup', function () {
return _.debounce(debounceIt, 500, false); //Why does this differ from #function
});
$('#noReturnAnonFunction').on('keyup', function () {
_.debounce(debounceIt, 500, false); //Not being executed
});
$('#exeDebouncedFunc').on('keyup', function () {
_.debounce(debounceIt, 500, false)(); //Executing the debounced function results in wrong behaviour
});
$('#function').on('keyup', _.debounce(debounceIt, 500, false)); //This is working.
});
function debounceIt(){
$('#output').append('debounced');
}
anonFunction
and noReturnAnonFunction
does not fire the debounce function; but the last function
does fire. I do not understand why this is. Can anybody please help me understand this?
EDIT
Ok, so the reason that the debounce does not happen in #exeDebouncedFunc (the one you refer) is because the function is executed in the scope of the anonymous function and another keyup event will create a new function in another anonymous scope; thus firing the debounced function as many times as you type something (instead of firing once which would be the expected behaviour; see beviour of #function
)?
Can you please explain the difference between #anonFunction
and the #function
. Is this again a matter of scoping why one of them works and the other does not?
EDIT Ok, so now I understand why this is happening. And here is why I needed to wrap it inside an anonymous function:
Fiddle: http://jsfiddle.net/6hg95/5/
HTML
<input id='anonFunction'/>
<div id='output'></div>
JAVASCRIPT
(function(){
var debounce = _.debounce(fireServerEvent, 500, false);
$('#anonFunction').on('keyup', function () {
//clear textfield
$('#output').append('clearNotifications<br/>');
debounce();
});
function fireServerEvent(){
$('#output').append('serverEvent<br/>');
}
})();
Upvotes: 100
Views: 129411
Reputation: 51
Came across this while looking for a solution to calling a debounce with a trailing call, found this article which really helped me: https://newbedev.com/lodash-debounce-not-working-in-react specifically:
Solution for those who came here because throttle / debounce doesn't work >with FunctionComponent - you need to store debounced function via useRef():
export const ComponentName = (value = null) => {
const [inputValue, setInputValue] = useState(value);
const setServicesValue = value => Services.setValue(value);
const setServicesValueDebounced = useRef(_.debounce(setServicesValue, 1000));
const handleChange = ({ currentTarget: { value } }) => {
setInputValue(value);
setServicesValueDebounced.current(value);
};
return <input onChange={handleChange} value={inputValue} />;
};
Upvotes: 5
Reputation: 966
As Palpatim explained, the reason lies in the fact that _.debounce(...)
returns a function, which when invoked does its magic.
Therefore in your #anonFunction
example, you have a key listener, which when invoked does nothing but return a function to the invoker, which does nothing with the return values from the event listener.
This is a snippet of the _.debounce(...)
definition:
_.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);
};
if (immediate && !timeout) func.apply(context, args);
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
Your key event listener must invoke the returned function from _.debounce(...)
, or you can do as in your non-anonymous example and use the returned function from the _.debounce(...)
call as your event listener.
Upvotes: 95
Reputation: 2402
Think easier
_.debounce returns a debounced function! So instead of thinking in terms of
$el.on('keyup'), function(){
_.debounce(doYourThing,500); //uh I want to debounce this
}
you rather call the debounced function instead
var doYourThingDebounced = _.debounce(doYourThing, 500); //YES, this will always be debounced
$el.on('keyup', doYourThingDebounced);
Upvotes: 89
Reputation: 513
You can return the debounce function like this:
(function(){
var debounce = _.debounce(fireServerEvent, 500, false);
$('#anonFunction').on('keyup', function () {
//clear textfield
$('#output').append('clearNotifications<br/>');
return debounce();
});
function fireServerEvent(){
$('#output').append('serverEvent<br/>');
}
})();
Upvotes: 7
Reputation: 1591
More generally, if you want a debounce with a trailing behaviour (accounts for last click, or more likely last change on a select input), and a visual feedback on first click/change, you are faced with the same issue.
This does not work:
$(document).on('change', "#select", function() {
$('.ajax-loader').show();
_.debounce(processSelectChange, 1000);
});
This would be a solution:
$(document).on('change', "#select", function() {
$('.ajax-loader').show();
});
$(document).on('change', "#select", _.debounce(processSelectChange, 1000));
Upvotes: 0
Reputation: 9262
debounce
doesn't execute the function, it returns a function with the debounciness built into it.
Returns
(Function): Returns the new debounced function.
So your #function
handler is actually doing the Right Thing, by returning a function to be used by jQuery as a keyup handler. To fix your #noReturnAnonFunction
example, you could simply execute the debounced function in the context of your function:
$('#noReturnAnonFunction').on('keyup', function () {
_.debounce(debounceIt, 500, false)(); // Immediately executes
});
But that's introducing a needless anonymous function wrapper around your debounce.
Upvotes: 37