Jhey Tompkins
3 min readJul 1, 2017

--

Hey worldwar!

Thanks for the response 😊

I’ve read over your response and I think you’re right👍 That version of throttle doesn’t quite work as expected every time. From having a look around, catching that last invocation can be a common oversight when implementing a version of throttle.

throttle can also have different variations of how it should behave. Should a function invoke right away or not? Do any of the throttled invocations get fired at the end?

To test, I decided to put two throttle invocations side by side. One using the article version of throttle and one using the lodash version of throttle.

// with article version
window.addEventListener('mousemove', throttle(() => {
console.info(`article: ${new Date().toUTCString()}`)
}, 2000))
// with lodash throttle
window.addEventListener('mousemove', _.throttle(() => {
console.info(`_: ${new Date().toUTCString()}`)
}, 2000))

I also, tested the version you suggested for throttle but both versions didn’t quite work as expected. The issue being that the last invocation will not fire at the time expected. If you had a throttle of 10 seconds and kept doing things until 5 seconds in, that last invocation would fire at 15 seconds since the last invocation if that makes sense.

This got me thinking 🤔

What is it we are actually doing with that throttle? When should that last invocation fire? and is this is actually just a chained debounce of sorts?

When we throttle we are limiting the amount something can happen by an interval. Take the analogy of ordering drinks at a bar. You go to a bar and the barman has a policy of only letting people order a drink every 30 minutes otherwise things get crazy. You order a drink in the 1st minute and you’re handed a drink. You try ordering a drink every minute thereafter and the barman says no until the 30th minute when he takes that 30th minute order. In the last invocation scenario, we order a drink say every minute until the 15th and then give up. But on the 30th minute the barman sends over a waiter with the drink from the 15th order, he feels sorry for you. He didn’t wait til the 45th minute.

So how is this like a debounce? In the sense that every time we try to invoke in the throttle we actually say no and create a setTimeout of the remaining time from our interval since the last time the function was invoked. That ensures that last time is ran when we expect. (This was later confirmed to me when I had a look around the lodash source and saw that throttle actually just returns an invocation of debounce with some set configuration.)

So how does a new version of throttle look?

var throttle = function(func, limit) {
var inThrottle,
lastFunc,
lastRan;
return function() {
var context = this,
args = arguments;
if (!inThrottle) {
// run the function immediately
func.apply(context, args)
// set a timestamp for invocation
lastRan = Date.now()
// put into throttle status
inThrottle = true
} else {
// like a debounce, clear the last invocation
clearTimeout(lastFunc)
// create that setTimeout with the correct interval
lastFunc = setTimeout(function() {
if ((Date.now() - lastRan) >= limit) {
func.apply(context, args)
// set a new running timestamp
lastRan = Date.now()
}
}, limit - (Date.now() - lastRan))
}
};
};

And that’s it, now when I run them side by side I get as expected with the same timestamps and running the same amount of times 👍😎

Thanks for the response again and hope that helps!

--

--

Jhey Tompkins
Jhey Tompkins

Written by Jhey Tompkins

I make awesome things for awesome people!

Responses (1)