How can a function rate-limit its calls? The calls should not be discarded if too frequent, but rather be queued up and spaced out in time, X milliseconds apart. I've looked at throttle and debounce, but they discard calls instead of queuing them up to be run in the future.
Any better solution than a queue with a process()
method set on an X millisecond interval? Are there such standard implementations in JS frameworks? I've looked at underscore.js so far - nothing.
Should be rather simple without a library:
var stack = [],
timer = null;
function process() {
var item = stack.shift();
// process
if (stack.length === 0) {
clearInterval(timer);
timer = null;
}
}
function queue(item) {
stack.push(item);
if (timer === null) {
timer = setInterval(process, 500);
}
}
http://jsfiddle.net/6TPed/4/
Here is an example which carries forward this
(or lets you set a custom one)
function RateLimit(fn, delay, context) {
var canInvoke = true,
queue = [],
timeout,
limited = function () {
queue.push({
context: context || this,
arguments: Array.prototype.slice.call(arguments)
});
if (canInvoke) {
canInvoke = false;
timeEnd();
}
};
function run(context, args) {
fn.apply(context, args);
}
function timeEnd() {
var e;
if (queue.length) {
e = queue.splice(0, 1)[0];
run(e.context, e.arguments);
timeout = window.setTimeout(timeEnd, delay);
} else
canInvoke = true;
}
limited.reset = function () {
window.clearTimeout(timeout);
queue = [];
canInvoke = true;
};
return limited;
}
Now
function foo(x) {
console.log('hello world', x);
}
var bar = RateLimit(foo, 1e3);
bar(1); // logs: hello world 1
bar(2);
bar(3);
// undefined, bar is void
// ..
// logged: hello world 2
// ..
// logged: hello world 3
While the snippets offered by others do work (I've built a library based on them), for those who want to use well-supported modules, here are the top choices:
- the most popular rate limiter is limiter
- function-rate-limit has a simple API that works, and good usage stats on npmjs
- valvelet, a newer module, claims to do an even better job by supporting promises, but hasn't gained popularity yet