限制 JavaScript 函数调用,但需要排队(不要丢弃调用)

Posted

技术标签:

【中文标题】限制 JavaScript 函数调用,但需要排队(不要丢弃调用)【英文标题】:Throttle JavaScript function calls, but with queuing (don't discard calls) 【发布时间】:2014-05-29 03:56:04 【问题描述】:

函数如何限制其调用的速率?如果调用过于频繁,则不应丢弃调用,而是要排队并及时间隔,相隔 X 毫秒。我查看了throttle 和debounce,但它们丢弃了调用,而不是将它们排队等待将来运行。

有比在 X 毫秒间隔上设置 process() 方法的队列更好的解决方案吗? JS框架中有这样的标准实现吗?到目前为止,我已经查看了 underscore.js - 没有。

【问题讨论】:

间隔定时器方法有什么问题? @Petah:原则上没什么,但我不想重新发明***。 它几乎没有重新发明***,应该是 我不喜欢这里的 "interval" 这个词,特别是如果您正在做的事情有任意处理要求。使用timeout这个词,你的措辞将与你应该如何编写代码相匹配(使用setTimeout @PaulS.:我想到的setInterval 的用途是处理队列,就像在this example 中一样。 【参考方案1】:

没有库应该相当简单:

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/

【讨论】:

感谢您的明确答复。我已经将它与@PaulS. 的答案结合起来以返回一个有限的函数,并且我已经进行了更改以立即启动队列(因为setInterval 首先延迟)。 Here's the fiddle.你怎么看?【参考方案2】:

这是一个继承 this 的示例(或让您设置自定义)

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;

现在

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

【讨论】:

我已将此答案与@Petah 使用setInterval 的解决方案结合起来。 Here's the fiddle.你怎么看?【参考方案3】:

虽然其他人提供的 sn-ps 确实有效(我基于它们构建了一个 library),但对于那些想要使用支持良好的模块的人来说,这里是首选:

最流行的限速器是limiter function-rate-limit 有一个简单的 API 可以工作,并且在 npmjs 上有很好的使用统计 valvelet,一个较新的模块,声称通过支持 Promise 做得更好,但尚未普及

【讨论】:

提到的现有答案和库不能满足我的需要,所以我又写了一个:debounce-queue。它的独特之处在于它提供了所有先前项目的数组,而不仅仅是最后一个/第一个。【参考方案4】:

我需要一个 TypeScript 版本,所以我使用了 @Dan Dascelescu 的 fiddle 并为其添加了类型。

如果您可以改进打字,请发表评论?

function rateLimit<T>(
  fn: (...args: Array<any>) => void,
  delay: number,
  context?: T
) 
  const queue: Array< context: T; arguments: Array<any> > = []
  let timer: NodeJS.Timeout | null = null

  function processQueue() 
    const item = queue.shift()

    if (item) 
      fn.apply<T, Array<any>, void>(item.context, item.arguments)
    

    if (queue.length === 0 && timer) 
      clearInterval(timer)
      timer = null
    
  

  return function limited(this: T, ...args: Array<any>) 
    queue.push(
      context: context || this,
      arguments: [...args],
    )

    if (!timer) 
      processQueue() // start immediately on the first invocation
      timer = setInterval(processQueue, delay)
    
  

【讨论】:

以上是关于限制 JavaScript 函数调用,但需要排队(不要丢弃调用)的主要内容,如果未能解决你的问题,请参考以下文章

排队系统设计思考

在 Wordpress 插件中排队 Javascript 和 jQuery

由于每秒上限而限制和排队 API 请求

如果再次调用 Javascript 函数,如何杀死它?

ChatGPT专业版可以排队了!限制更少但收费,网友:钱已备好

jQuery.when() 是不是对这些函数调用进行排队?