3

Just title says. Most throttle approaches in javascript require some sort of a variable to store the timer or setTimeout id to determine if a function is to be cancelled or not. However im having some difficulty in wrapping into my head on how do I exactly write this function in functional way.

const throttle = (func) => {
  let handler = null;

  return (...args) => {


    if (handler === null) {

      handler = setTimeout(() => {
        handler = null
      }, 2000)

      return func(...args)
    } else {
      return null
    }
  }
}

In this code, It seems to break the rules of avoiding side effects since the return function is mutating the variable inside the throttle scope. How do I exactly convert this logic into a pure function(s)?

Amia
  • 96
  • 6
  • This depends **a lot** on how you model side effect, and in particular the passing of time, in your functional program. Which is a non-trivial task. Are you familiar with FRP for example? – Bergi Nov 08 '22 at 02:35
  • I'm not really familliar of FRP, any resource for that? – Amia Nov 08 '22 at 03:45
  • https://www.google.com/search?q=functional+reactive+programming&oq=functional+reac&aqs=chrome.1.69i57j69i59j0i433i512j0i20i263i512j0i512l2j69i60j69i65.2876j0j7&sourceid=chrome&ie=UTF-8 – Jared Smith Nov 08 '22 at 14:17
  • 2
    The short answer is don't feel bad because you're struggling with this, as Bergi said it can be difficult to model the concept of time in a purely functional way. "It seems to break the rules of avoiding side effects" it breaks more rules than that: because it's not memoized you will return a different function for the same input every time, and the return type of the func argument is `void` which is hardly functional. This is not an easy problem to solve! – Jared Smith Nov 08 '22 at 14:21
  • I guess that makes sense for me now, thanks! – Amia Nov 09 '22 at 03:03

1 Answers1

2

I hope this answer isn't too much opinion based, but I think that any meaningful program will contain a considerable amount of side effects. Functional Programming is rather about managing them than evading/banning them.

For the specific case of throttling / rate limiting a function call, I'd suggest to make sure that your inner function is pure... And wrap it in a function that implements rate limiting generically.

const sum = (a, b) => a + b;

const rateLimit = (ms, fn) => {
  let redlight;
  
  return (...args) => {
    if (redlight) {
      return;
    }

    redlight = true;
    setTimeout(() => {
      redlight = false;
    }, ms);
    
    return fn(...args);
  };
}

const throttled = rateLimit(100, sum);

console.log(
  throttled(1, 2),
  throttled(2, 4), //=> undefined
  throttled(5, 6), //=> undefined
)

setTimeout(() => console.log(throttled(10, 10)), 500);

I'd suggest reading about monads as the common way to model side effects in functional programming.

Then, as suggested in other answers/comments, you can replace your rateLimit with something that is already available to the industry (e.g. RFP libraries, etc.)

Hitmands
  • 13,491
  • 4
  • 34
  • 69