如何对ajax请求进行速率限制?

18

有几个div和处理程序,当它们被点击时会发送ajax请求。我的问题是我不知道如何强制处理程序不超过每30秒1个请求的限制。

感谢您的帮助!

3个回答

36

Underscore.js有一个优秀的节流函数。你传入要节流的处理程序,就会得到一个速率受限制的同一函数的版本。

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

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

这是我在自己的代码中使用的简化版本:

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);
        }
    }
}
一个测试它的好方法是触发大量滚动事件并观察您的处理程序记录到Firebug控制台中:
document.addEventListener("scroll", throttle(function() {
    console.log("test");
}, 2000), false); 

以下是一个版本,使用jQuery限制每30秒内只能点击一次div:

$("div").click(throttle(function() {
    // ajax here
}, 30000));

1
+1 对于underscore用户来说是一个很好的解决方案。我也强烈推荐使用underscore。 - Tauren
如果您不传递事件处理程序而需要直接调用它,请不要忘记像这样调用它:throttle(function() { // ajax here }, 30000)(); - Luke Stanley
2
_.throttle 用于限制在指定时间内调用函数的次数。如果您想要进行速率限制,即排队请求并确保它们每 X 秒只运行一次,则需要另一个解决方案(将在我的答案中提供)。 - Matthew O'Riordan
_.throttle通常是您想要的。您排队一堆事件驱动回调以在事件发生后长时间执行的用例是什么? - Wayne
太棒了。还要看一下_.debounce()——对于我的类似相关情况,它对于搜索输入的回调函数更好。 - Jordan Sitkin
显示剩余2条评论

19

如果您想进行速率限制,那么不幸的是underscore.js提供的_.throttle方法不是您的解决方案。此方法只会确保您的方法在X秒内不会被调用多次,因此所有随后的函数调用都将被忽略,直到该时间段过去。

如果您想对函数进行速率限制,以便每秒钟不要超过X次调用该函数,但又不想完全丢失这些函数调用,则需要完全不同的解决方案。

我编写了一个underscore扩展程序,可以在https://gist.github.com/1084831找到。

您可以在http://jsbin.com/upadif/8/edit#preview上看到一个工作示例。


我怀疑这不是OP所问的,但对于一个有趣的插件+1。 - Wayne
6
谢谢,这正是我要寻找的。(应用场景是基于单个用户操作触发多个查询到受限制的第三方API。) - Jack Cushman

11
创建一个布尔类型的canFireRequest标志,或者其他名字的标志,并在每个ajax请求后将其设置为false。然后创建一个30秒的时间间隔,将其重新设置为true;在每个新请求之前检查标志的值。
这里是一个简单的示例:
if ($(this).data('canFireRequest')) {
    // Ajax request goes here
    $(this).data('canFireRequest', false);
}

setTimeout(function() {
    $(this).data('canFireRequest', true)
}, 30000);

2
setTimeout的第二个参数应该是30000,表示30秒。 - unclenorton
@unclenorton,哎呀!确实!已修复! - Mohamad
这不是一个很好的解决方案,因为它没有将受限制的请求放入队列,它只是简单地忽略任何在规定时间内到达的请求。 - Matthew O'Riordan
6
@Mathew,这个问题没有说明是否需要排队后续的请求或只是简单地忽略它们。这是否是一个好的解决方案取决于您的需求。例如,SO允许您在5秒内进行一次评论投票。他们不会排队后续的投票请求,而是直接忽略它们并提供错误消息。再次强调,这取决于您的需求。 - Mohamad
应该使用setInterval。setTimeout只会触发一次。 - Balasubramanian Ramamoorthi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接