Skip to content

Instantly share code, notes, and snippets.

@jasonrhodes
Last active February 24, 2018 21:44
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jasonrhodes/11f6a9f27e4b115343cca5af2883f918 to your computer and use it in GitHub Desktop.
Save jasonrhodes/11f6a9f27e4b115343cca5af2883f918 to your computer and use it in GitHub Desktop.
I want to throttle a function but split up by args, is there an easy way?

Say you have a function that takes a single argument like this:

// return a string date that is "n" days before "right now"
function backInTime(n) {
  return subtract(new Date(), n, 'days').toString();
}

So if you call this (on Thurs, Feb 22) a few times in a row (pretending each one takes a full second to run) you would get:

backInTime(1) // "Wed Feb 21 2018 20:55:05 GMT-0500 (EST)"
backInTime(1) // "Wed Feb 21 2018 20:55:06 GMT-0500 (EST)"
backInTime(1) // "Wed Feb 21 2018 20:55:07 GMT-0500 (EST)"

(notice the seconds incrementing in the output)

I want to throttle that function so that it only gets calculated once every 5 seconds, for example.

I can use Lodash's _.throttle to achieve this (again assuming each one takes a full second to run):

const throttled = _.throttle(backInTime, 5000)

throttled(1) // "Wed Feb 21 2018 20:55:05 GMT-0500 (EST)"
throttled(1) // "Wed Feb 21 2018 20:55:05 GMT-0500 (EST)"
throttled(1) // "Wed Feb 21 2018 20:55:05 GMT-0500 (EST)"
throttled(1) // "Wed Feb 21 2018 20:55:05 GMT-0500 (EST)"
throttled(1) // "Wed Feb 21 2018 20:55:05 GMT-0500 (EST)"
throttled(1) // "Wed Feb 21 2018 20:55:10 GMT-0500 (EST)"

Yay! Except, what happens when you call it with different arguments now?

throttled(1) // "Wed Feb 21 2018 20:55:05 GMT-0500 (EST)"
throttled(5) // "Wed Feb 21 2018 20:55:05 GMT-0500 (EST)"

Crap.

Is there an easy way to get a form of "memoization" mixed with "throttling"? I want to throttle but only for calls with the same argument, but I feel like this is going to take some hack shenanigans. :/

What I want is this (still pretending that each call takes a full second!):

throttled(1) // "Wed Feb 21 2018 20:55:05 GMT-0500 (EST)"
throttled(5) // "Wed Feb 17 2018 20:55:06 GMT-0500 (EST)"
throttled(1) // "Wed Feb 21 2018 20:55:05 GMT-0500 (EST)"
throttled(2) // "Wed Feb 20 2018 20:55:08 GMT-0500 (EST)"
throttled(1) // "Wed Feb 21 2018 20:55:05 GMT-0500 (EST)"
throttled(1) // "Wed Feb 21 2018 20:55:10 GMT-0500 (EST)"
throttled(2) // "Wed Feb 20 2018 20:55:08 GMT-0500 (EST)"
@MylesBorins
Copy link

MylesBorins commented Feb 23, 2018

what about

let throttled;
let last;
function backInTime(days) {
  if (days !== last) throttled = _.throttle(backInTimeCalc, 5000);
  return throttled(days);
}
function backInTimeCalc(days) {
  return subtract(new Date(), days, 'days');
}

which could refactored to

let throttled;
let last;
function backInTime(days) {
  if (days !== last) {
    throttled = _.throttle((days) => {
      return subtract(new Date(), days, 'days');
    }, 5000);
  }
  return throttled(days);
}

@jasonrhodes
Copy link
Author

jasonrhodes commented Feb 23, 2018

@MylesBorins yeah nice, I was trying to avoid re-creating the throttled functions over and over so I think I'm going to end up building my own mini memoize implementation ... a la

const cache = {};
function backInTime(days) {
  if (!cache[days]) {
    cache[days] = _.throttle(() => backInTimeCalc(days), 5000);
  }
  return cache[days]()
}

function backInTimeCalc(days) {
  return subtract(new Date(), days, 'days');
}

🤔 that's not bad I guess?

@nancyhabecker
Copy link

You could bust memoize's cache, which feels a little icky but hey, free cache!

const backInTime = _.memoize(function(a) {
  _.delay(() => {
    backInTime.cache.delete(a);
  }, 5000);
  return backInTimeCalc(a);
});

@jed
Copy link

jed commented Feb 23, 2018

personally, i would simplify things by separating the memoizing from the read.

const five = backInTime(5)
// time passes
five.read()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment