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!

Wayne
  • 59,728
  • 15
  • 131
  • 126
allis
  • 181
  • 1
  • 1
  • 3

3 Answers3

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));
Wayne
  • 59,728
  • 15
  • 131
  • 126
  • 1
    +1 Great solution for underscore users. I highly recommend underscore as well. – Tauren Mar 22 '11 at 11:28
  • 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)(); – Luke Stanley Jul 08 '11 at 12:15
  • 2
    _.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) – Matthew O'Riordan Jul 15 '11 at 14:53
  • `_.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? – Wayne Jul 15 '11 at 15:32
  • Fantastic. also look into _.debounce() - it was better for my similar, related case of a callback function for a search input. – Jordan Sitkin Sep 25 '12 at 15:46
  • @DustMason - The difference is that a debouncer resets the clock every time the event fires. But, yes, also very useful. – Wayne Sep 25 '12 at 16:02
  • Won't this `throttle()` function swallow events while a timer is running? – M4N Jun 03 '14 at 11:20
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

Matthew O'Riordan
  • 7,981
  • 4
  • 45
  • 59
  • I suspect this is not what the OP was asking for, but +1 for an interesting plugin. – Wayne Jul 15 '11 at 15:33
  • 6
    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.) – Jack Cushman Aug 29 '12 at 21:30
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);
Mohamad
  • 34,731
  • 32
  • 140
  • 219
  • 2
    The second parameter of setTimeout should be 30000 for 30 seconds, I guess – unclenorton Feb 17 '11 at 20:27
  • @unclenorton, eeeeek, indeed! Fixed! – Mohamad Feb 17 '11 at 21:58
  • 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. – Matthew O'Riordan Jul 15 '11 at 14:53
  • 6
    @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. – Mohamad Jul 15 '11 at 15:03
  • It should be setinterval. settimeout fires only once – Balasubramanian Ramamoorthi Oct 12 '21 at 10:04