I’m working through a Javascript course and am working with the following code. I understand almost all of it but I’m struggling with following and understanding the logic flow through the code, particularly for the event objects and I want to make sure I’m super clear on this before continuing.
Almost the exact same question has been asked here by someone else with the same confusion but I can’t make sense of any answers unfortunately.
Here’s what I do understand so far:
A key gets pressed -> The debounce
function returns (and runs) with parameters func
and delay
. The func
parameter passed in is the onInput
function in this case, (which as I understand it, gets an event object returned to it automatically (by Javascript) when the addEventListener
fires).
However, onInput
is run nested inside the debounce
function with func.apply(null, args);
so I’m confused as to how the event objects get created and passed through the flow of the code when keys are pressed?
My main question following from this, is how or where does return (...args)
within the debounce
get its spread parameters from?
Doesn’t the debounce
function get passed the event object in this case and not onInput
? If so, how does onInput
get access to the event object?
Here’s the code:
const fetchData = async (searchTerm) => { const response = await axios.get('http://www.omdbapi.com/', { params: { apikey: '6459bge8', s: searchTerm } }); console.log(response.data); }; const input = document.querySelector('input'); const debounce = (func, delay) => { let timeoutId; //Where is ...args getting it's values from? return (...args) => { console.log(args); if (timeoutId) { clearTimeout(timeoutId); } timeoutId = setTimeout(() => { func.apply(null, args); }, delay); }; }; const onInput = (evt) => { fetchData(evt.target.value); }; input.addEventListener('input', debounce(onInput, 500));
Also I can’t make sense of when I comment out the code within the returned function like this:
const debounce = (func, delay) => { let timeoutId; return (...args) => { console.log(args); // if (timeoutId) { // clearTimeout(timeoutId); // } // timeoutId = setTimeout(() => { // func.apply(null, args); // }, delay); }; };
The passed in func
never runs but the console.log(args) still shows InputEvents
in the console when a key is pressed suggesting the args are coming from elsewhere and not set by func.apply(null, args);
?
Advertisement
Answer
The main thing to understand with your code is that the addEventListener()
function isn’t in charge of calling the debounce()
function. The debounce()
function is called when the addEventListener
gets added to the input element, not when the input event occurs. This is because calling debounce()
invokes the function, passing whatever it returns as the second argument to addEventListener()
. With that in mind, you function can be re-written as this:
const inputHandler = debounce(onInput, 500); // debounce returns a function input.addEventListener('input', inputHandler); // the returned function is used in the addEventListener function
So the function that is returned by debounce()
is called when an input occurs (not the debounce
function itself, as this is called when the addEventListener() method is called, which is immediately when the interpreter meets this line and not when an input occurs).
Doesn’t the debounce function get passed the event object in this case and not onInput? If so, how does onInput get access to the event object?
With the above explanation in mind, the returned function from debounce()
is what gets passed as the second argument to addEventListener()
. As a result, the returned function acts as the callback and gets passed the event object, which it has accesses to through ...args
. In the code-block above, that means inputHanlder
gets passed the event object when it gets invoked by JS when an input event occurs. So debounce()
never gets passed the event argument, it’s the inner returned function that gets access to the event argument.
As the returned function (ie: the inner function in your code example), gets passed the event object, it can access it via args
. The inner function then invokes/calls the onInput
function with the event object using func.apply(null, args);
.
As for your last example, the func
function never runs as it is never called anywhere. It is passed into your function, but it never gets invoked/executed (unlike in the first example where it does get called using .apply()
). The InputEvent still gets logged though, as the addEventListener()
is what invokes the callback returned when the input occurs. As a result, the inner function still has access to the event object.