0

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);?

sideshowbarker
  • 81,827
  • 26
  • 193
  • 197
Diagonali
  • 109
  • 1
  • 10
  • It gets those args from whatever calls it. debounce is a *higher order function*, it returns a function that in this case is passed as the callback to addEventListener. – jonrsharpe Feb 25 '21 at 09:16

1 Answers1

0

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.

Nick Parsons
  • 45,728
  • 6
  • 46
  • 64
  • Thanks very much for your detailed reply. I've worked through and think the penny has dropped: The anonymous function within the `debounce` function effectively runs "in-place" of the 'debounce(onInput, 500)' call in `addEventListener`. This means that Javascript passes the event object created on 'input' into the anonymous function instead of the `debounce` function. That's where the `(...args)` gets it's value(s) from, which in this case is (only?) an event object(?). I'm still confused however that `debounce` gets called on page load as I thought it only gets run on 'input'? – Diagonali Feb 26 '21 at 10:35
  • @Diagonali No worries, sounds like you understand it a little better now. There is a difference between a _function call_ (`someFunc()`), and a function reference (one yet to be called) ie: `someFunc`. When JS sees `someFunc()` the `()` at the end of it tells JS to invoke `someFunc()`, irrespective of where it's being called. Since you are _calling_ `debounce()`, JS will invoke it when the page loads and provide / whenever it comes across the function call. If you were to pass just `debounce (and not the called version of it), then it would only be called by JS when an input occurs. – Nick Parsons Feb 26 '21 at 10:51
  • I gave a similar answer here a few days ago which you might also want to take a look that explains the steps that occurs when JS comes across the call to `.addEventListener()`: [why does the event listener in this throttling function behave like that?](https://stackoverflow.com/a/66289434) it might clear some things up. (oh, and yes, `event` is the only argument passed to the callback) – Nick Parsons Feb 26 '21 at 10:52
  • 1
    Ah, that explains the difference between function calls and references perfectly. It's pretty obvious now you point it out, I probably should have remembered that! I'll check out the other answer you gave for more info. I can continue with the course now knowing I'm not skipping over understanding anything! Appreciate your help. – Diagonali Feb 26 '21 at 13:04