How to rate-limit ajax requests?
Asked Answered
T

3

18

There are several divs and handler to send ajax requests when they are clicked. My problem is that i don't know how to force my handler not to exceed limit of 1 request per 30 seconds.

Appreciate your help!

Tannen answered 17/2, 2011 at 16:11 Comment(0)
C
36

The excellent Underscore.js has a throttle function. You pass in the handler that you want to throttle and get back a rate-limited version of the same function.

var throttled = _.throttle(someHandler, 100);
$(div).click(throttled);

http://documentcloud.github.com/underscore/#throttle

Here's a simplified version that I've used in my own code:

function throttle(func, wait) {
    var timeout;
    return function() {
        var context = this, args = arguments;
        if (!timeout) {
            // the first time the event fires, we setup a timer, which 
            // is used as a guard to block subsequent calls; once the 
            // timer's handler fires, we reset it and create a new one
            timeout = setTimeout(function() {
                timeout = null;
                func.apply(context, args);
            }, wait);
        }
    }
}

A good way to test it is by firing off a bunch of scroll events and watching your handler log to the Firebug console:

document.addEventListener("scroll", throttle(function() {
    console.log("test");
}, 2000), false); 

Here's a version that limits click-events on divs to once every 30 seconds, as requested (requires jQuery):

$("div").click(throttle(function() {
    // ajax here
}, 30000));
Corson answered 17/2, 2011 at 22:8 Comment(7)
+1 Great solution for underscore users. I highly recommend underscore as well.Varicolored
or if you need to call it directly because you aren't passing an event handler, don't forget to call it like this: throttle(function() { // ajax here }, 30000)();Motorcade
_.throttle is used to limit the number of function calls within a specified amount of time. If you want to rate limit i.e. queue up requests and ensure they only run every X seconds, then you will need another solution (to be provided in my answer)Appellative
_.throttle is usually what you want. What is your use-case for queuing-up a bunch of event-driven callbacks to execute long after the event has occurred?Corson
Fantastic. also look into _.debounce() - it was better for my similar, related case of a callback function for a search input.Irrefrangible
@DustMason - The difference is that a debouncer resets the clock every time the event fires. But, yes, also very useful.Corson
Won't this throttle() function swallow events while a timer is running?Armorer
D
19

If you want to rate limit, then unfortunately the _.throttle method that underscore.js provides is not your solution. Throttle will simply ensure your method is never called more than X seconds, and therefore all subsequent function calls will be disregarded until that period has passed.

If you want to rate limit so that you never call your function more than X times per second, but don't lose those function calls altogether, then you need a wholly different solution.

I have written an underscore extension at https://gist.github.com/1084831

You can see a working example at http://jsbin.com/upadif/8/edit#preview

Dulia answered 15/7, 2011 at 14:56 Comment(2)
I suspect this is not what the OP was asking for, but +1 for an interesting plugin.Corson
Thanks -- this was what I was looking for. (The use case being to trigger multiple queries to a third-party, rate-limited API based on a single user action.)Wayworn
P
11

Create a boolean canFireRequest, or whatever, flag and set it to false after each ajax request. Then create a 30 second time span that sets it back to true; check the flag's value before each new request.

Here's a rough example:

if ($(this).data('canFireRequest')) {
    // Ajax request goes here
    $(this).data('canFireRequest', false);
}

setTimeout(function() {
    $(this).data('canFireRequest', true)
}, 30000);
Pathological answered 17/2, 2011 at 16:12 Comment(5)
The second parameter of setTimeout should be 30000 for 30 seconds, I guessWessel
@unclenorton, eeeeek, indeed! Fixed!Pathological
This is not a great solution as it does not queue the rate limited requests, it simply ignores any request that come in the specified amount of time.Appellative
@Mathew, the question did not indicate whether there is a requirement to queue subsequent requests or simply ignore them. Whether this is a good solution or not depends on your need. For example, SO allows you one comment vote every 5 seconds. They don't queue subsequent vote requests. They just ignore them and provide you with an error message. Again, it depends on your need.Pathological
It should be setinterval. settimeout fires only onceGodfearing

© 2022 - 2024 — McMap. All rights reserved.