Reputation: 51
So, I´m using the .throttle method in Javascript, to avoid spaming a function. Even if the method would be called often, there would be a certain time between these calls. Here is my code:
const _ = require('lodash');
const minTimeBetweenAlerts = 2000;
const tester = _.throttle(test, minTimeBetweenAlerts);
function test() {
console.log('function called!');
}
for(var i = 0; i < 3; i++) {
tester();
}
As you can see, a quite easy code. But I´ve noticed, that if tester(); would be called more than 2 times (for example 3 times in a row in a very very short amount of time). The problem is: After running my code, the output in my console would be only 2 times "function called". Has anyone a suggestion, how I can fix this problem ?
EDIT: My goal is that, the function will be called 3 times instead of 2. But I want to keep the minTime between the calls of the function
Upvotes: 0
Views: 173
Reputation: 55729
The behavior of lodash throttle is to invoke the function, and then by default invoke it again at the end (the trailing edge) of the interval if a subsequent call has been made within the interval. That is why you get two invocations.
In the following diagram "call" means you call the throttled function, and "invoke" means the underlying function is run. Note how one of the calls is discarded:
0 1000ms 2000ms
|______________|______________|
^ ^ ^ ^
| | | |
invoke | | invoke (trailing invocation)
| | |
| | |
call call call
What you want is a rate limiter function. I had a stab at one below, but I'd recommend using a library implementation.
function limit(f, interval) {
const q = []
let timeLastRun = 0
let currentTimeoutId = null
const maybeRun = () => {
if(!q.length) return;
const now = performance.now()
if((now - timeLastRun) >= interval) {
clearTimeout(currentTimeoutId)
currentTimeoutId = null
timeLastRun = now
q.pop()()
if(q.length) {
currentTimeoutId = setTimeout(maybeRun, interval)
}
}
}
const limited = (...args) => {
q.unshift(() => f(...args))
maybeRun()
if(q.length && !currentTimeoutId) {
currentTimeoutId = setTimeout(maybeRun, interval)
}
}
return limited
}
function test() {
console.log('function called!')
}
const tester = limit(test, 2000)
for(var i = 0; i < 3; i++)
tester()
Upvotes: 2