I can't get a simple throttle function to work

614 views Asked by At

I know there is a bunch of other people who have posted questions about throttling functions and I have scrolled through them but most if not all are way above my level or include stuff like jquery and really weird logic to function.

I'm just trying to limit the number of changes a user can make per second, to stop them from spamming stuff.

I wrote my code with the help of this youtube video and I can understand it, for the most part. However it doesn't seem to work, I can't see any issues or blocks.

This is my first attempt at implementing throttling:

const throttle = (bad_func, limit) =>{
var flag = true;
document.getElementById('key').innerHTML = flag;
return function(){
  let context = this;
  let args = arguments;
  if(flag){
    bad_func.apply(context,args);
    bad_func();
    flag = false;
    setTimeout(()=>{
      flag = true;
    },limit);
  }
}
}

 ThrottledFunc = throttle(logKey, 4000);
 window.addEventListener('keydown', ThrottledFunc);

  function logKey(e){
  //           document.getElementById('EKey').innerHTML = e.which;
    if (e.which == 87){
     document.getElementById('demo').innerHTML = 'forwards';
     }

    else if (e.which == 83){
     document.getElementById('demo').innerHTML = 'backwards';
     }

    else{
     document.getElementById('demo').innerHTML = 'empty';
     }

     }

But it doesn't work, I can still spam w and s. The "demo" changes but there is no delay.

For my second attempt I just said screw it and tried to implement the timeout thing into the function, still no luck:

window.addEventListener('keydown', logKey);

function logKey(e){
var flag = true;
var limit = 10000;
document.getElementById('key').innerHTML = flag;
if(flag){
  if (e.which == 87){
    flag = false;
    document.getElementById('demo').innerHTML = 'forwards';
    setTimeout(()=>{
      flag =true;
    }, limit);
  }

  else if (e.which == 83){
    document.getElementById('demo').innerHTML = 'backwards';
    flag = false;
    setTimeout(()=>{
      flag =true;
    }, limit);
   }
  }

else{
   document.getElementById('demo').innerHTML = 'empty';
  }
 }

What am I doing wrong?

2

There are 2 answers

0
iY1NQ On BEST ANSWER

You need to build a closure, that means the variable flag must preserve its value between each logKey() invocation. The solution is to store it global (as below) or in a parent scope where logKey can access it.

window.addEventListener("keydown", logKey);

var flag = true;
var limit = 10000;

function logKey(e) {

  document.getElementById("key").innerHTML = flag;
  
  if (flag) {
    
    if (e.which == 87) {
      
      flag = false;
      document.getElementById("demo").innerHTML = "forwards";
      
      setTimeout(() => {
        flag = true;
      }, limit);
      
    } else if (e.which == 83) {
      
      document.getElementById("demo").innerHTML = "backwards";
      flag = false;
      
      setTimeout(() => {
        flag = true;
      }, limit);
      
    }
  } else {
    document.getElementById("demo").innerHTML = "empty";
  }
}
<div id="demo"></div>
<div id="key"></div>


I would suggest using a library like lodash that provides a throttle function.

1
V Maharajh On

I went thru a similar exercise a couple years ago.

I ended up coming up with a really tiny implementation: throttled-event-listener.js

Here's a live demo that uses it.

And here's some docs on what the calling code looks like.

Hope this helps!