While I was learning about throttling in javascript, I came upon an event-listener behavior that I can’t explain. Here is a simple throttling function.
const btn=document.querySelector("#throttle"); // Throttling Function const throttleFunction=(func, delay)=>{ // Previously called time of the function let prev = 0; console.log(prev); //This gets called only once and it is even before the button is clicked return (...args) => { // Current called time of the function let now = new Date().getTime(); // Logging the difference between previously // called and current called timings console.log(now-prev, delay); //This however doesn't get called before the button is clicked // If difference is greater than delay call // the function again. if(now - prev> delay){ prev = now; // "..." is the spread operator here // returning the function with the // array of arguments return func(...args); } } } btn.addEventListener("click", throttleFunction(()=>{ console.log("button is clicked") }, 1500));
<button id="throttle">Click Me</button>
I have two questions.
- Why does
let prev = 0;
only get called once? i.e why doesn’tprev
get reset to 0 every time the function is called? - I have also noticed that
let prev = 0;
gets called before the button is even clicked, Why does this happen? and why doesn’t the rest of the function get called before the button is clicked as well?
This is where I found the code above. I have looked into addEventListener
on MDN but to no avail. Any help would be appreciated.
Advertisement
Answer
The .addEventListener()
method takes a reference to a function as its second argument, which it can invoke when your button is clicked. So something like this will fail to add the function as a click event handler:
const sayHello = () => console.log("hello"); btn.addEventListener("click", sayHello());
In the above example JavaScript:
Sees the call to
addEventListener()
Evaluates its arguments, which means calling the
sayHello()
function.2.1.
sayHello()
runs and returnsundefined
Calls the
addEventListener()
method with"click"
andundefined
(the evaluated arguments)
Above the sayHello()
is a function call, so it will execute as your event listener is added and before any clicks occur, resulting in the return value of sayHello
being used as the second argument to addEventListener()
, so the above code will evaluate to something like:
const sayHello = () => console.log("hello"); btn.addEventListener("click", undefined);
To correctly pass a reference to the event listener, you would need to pass a function that can then later be invoked by JS when a click occurs:
btn.addEventListener("click", sayHello);
With that in mind, your throttleFunction()
argument is being called when you add your event listener, which means that the throttleFunction
itself isn’t what is being passed as the second argument to addEventListener()
, but rather the return value. This may be a little clearer if you extract the callback to throttleFunction
:
const fn = () => {console.log("button is clicked")}; // invoking the `throttleFunction` function with `()` const clickHandler = throttleFunction(fn, 1500); btn.addEventListener("click", clickHandler); // clickHandler is a reference to a function (a non-invoked/called function)
Since your throttleFunction()
function is being invoked, the returned function from throttleFunction
is being used as the argument for addEventListener()
, not the throttleFunction itself. The returned function is only then executed when a click occurs. As a result, let prev = 0;
is executed once when the throttleFunction is first called which is when click event listener is added, but the returned function is executed multiple times, as JS will call it only when you click on your button.