Underscore: 建议:_.debounce 和 _.throttle 需要额外的参数来组合参数

创建于 2011-09-22  ·  11评论  ·  资料来源: jashkenas/underscore

如果我使用 _.debounce() 来创建一个去抖动函数,然后使用 3 组不同的参数连续调用它 3 次,那么(从 v1.1.7 开始)包装的有效负载函数最终将使用指定的参数调用第三次调用 - 即第一个和第二个参数被丢弃。

虽然这通常是有效的(并且键去抖动通常是这样做的,因此是一个合理的默认值)我发现自己想使用去抖动来累积参数,例如我有一个可以一次获取多个键的 AJAX 调用,所以我使用去抖动来缓冲向上键一秒钟,然后发出合并请求。

因此,我的建议是 debounce 采用可选的第三个“组合”参数,该参数将使用 2 个参数调用,

  • 第一个是迄今为止累积的 args 列表(可能未定义 - 即第一次调用时没有列表)
  • 第二个是最新调用的 args 列表(可能是一个空列表)
    并返回新的累积参数列表。 调用有效载荷函数时,会清除累积的 args 列表。

如果没有为 combine 参数传递值,则默认 combine 保留现有行为
function(acc, newargs) { return newargs; }
但您也可以决定使用第一组参数
function(acc, newargs) { return acc || newargs; }
或者我想做的就是简单地附加所有参数
function(acc,newargs) { return (acc || []).concat(newargs); }
当然其他人可能想做一些更花哨的事情

这将需要对内部限制功能进行以下更改

  // Internal function used to implement `_.throttle` and `_.debounce`.
  var limit = function(func, wait, debounce, combine) {
    var timeout, allargs;
    return function() {
      var context = this;
      allargs = combine(allargs,  slice.call(arguments,0))
      var throttler = function() {
        timeout = null;
        var args = allargs;
        allargs = undefined;
        func.apply(context, args);
      };
      if (debounce) clearTimeout(timeout);
      if (debounce || !timeout) timeout = setTimeout(throttler, wait);
    };
  };

然后更改去抖动以接受并通过具有默认值的新参数(如果未指定)。

  _.debounce = function(func, wait, combine) {
    return limit(func, wait, true, combine || function(acc,newargs) { return newargs; });
  };

相应的节流函数当前仅使用第一组参数(节流有效地忽略在第一次调用的等待毫秒内发生的调用并使用参数的第一个调用集,去抖动有效地忽略在等待期内发生的序列中除了最后一个调用之外的所有调用彼此),所以我建议下面再次保留当前的默认行为

  _.throttle = function(func, wait, combine) {
    return limit(func, wait, false, combine || function(acc,newargs) { return acc || newargs; });
  };

这似乎是实现此功能的最简单和最通用的方法,无需过多的包装器来维护参数列表,但我很想知道是否有一种简单的方法可以在不更改下划线的情况下实现这一点。

enhancement wontfix

最有用的评论

好的,不要继续敲打,但如果有人稍后看到这个并想知道如何做同样的事情,我认为这是不修改 debounce 本身的最干净的方式(我将它添加到 _ 对象,其他人可能不喜欢到)

_.mixin({
  debounceReduce: function(func, wait, combine) {
    var allargs,
        context,
        wrapper = _.debounce(function() {
            var args = allargs;
            allargs = undefined;
            func.apply(context, args);
        }, wait);
        return function() {
            context = this;
            allargs = combine.apply(context, [allargs,  Array.prototype.slice.call(arguments,0)]);
            wrapper();
        };
    }
})

这给出了一个 debounced 函数,它的参数由 combine 函数减少,例如,

  delayLog = _.debounceReduce(function() { console.log(arguments); }, 5000, 
                              function(acc,args) { return (acc || []).concat(args); });
  delayLog(3,4);
  delayLog(7,8,9);

几秒钟后将使用数组 [3,4,7,8,9] 调用 console.log

所有11条评论

评论我自己的建议,对 combine() 的调用应该指定与有效负载函数相同的上下文,因此

allargs = combine.apply(this, [allargs, slice.call(arguments,0)])

如果参数需要访问上下文对象....

现在应该固定在主人身上。 throttle应该表现出正确的行为,它始终使用您的参数的最新副本,立即触发一次,之后每隔N秒...并N秒后自行重置最后一个尾随触发已发生。

我认为您的密切评论适用于另一个问题(可能是 #170),因为此请求提出的问题仍然适用于 master。
仍然没有简单的方法可以从正在组合的调用中获得去抖动或节流累积参数,我仍然认为这是一个有用的可选添加,当未指定可选组合参数时,它会保持默认行为不变。

啊,你是对的。 累积参数超出了下划线的范围——随意将累积的数据存储在_.throttle_.debounce函数之外的好地方。

很遗憾,我认为去抖动是一种在超时的多次调用中的左折叠(减少),因此是累加器......但这是你的电话:)

好的,不要继续敲打,但如果有人稍后看到这个并想知道如何做同样的事情,我认为这是不修改 debounce 本身的最干净的方式(我将它添加到 _ 对象,其他人可能不喜欢到)

_.mixin({
  debounceReduce: function(func, wait, combine) {
    var allargs,
        context,
        wrapper = _.debounce(function() {
            var args = allargs;
            allargs = undefined;
            func.apply(context, args);
        }, wait);
        return function() {
            context = this;
            allargs = combine.apply(context, [allargs,  Array.prototype.slice.call(arguments,0)]);
            wrapper();
        };
    }
})

这给出了一个 debounced 函数,它的参数由 combine 函数减少,例如,

  delayLog = _.debounceReduce(function() { console.log(arguments); }, 5000, 
                              function(acc,args) { return (acc || []).concat(args); });
  delayLog(3,4);
  delayLog(7,8,9);

几秒钟后将使用数组 [3,4,7,8,9] 调用 console.log

@schmerg - 这看起来非常有用。 您愿意根据 MIT 许可许可该代码吗? (一个“是”就足够了!)

@markjaquith当然可以 - 是的。 乐于...

如果有人出现并想要更新/评论上述现代 js 版本:

_.mixin({
  debounceReduce(func, wait, combine) {
    let allArgs; // accumulator for args across calls

    // normally-debounced fn that we will call later with the accumulated args
    const wrapper = _.debounce(() => func(allArgs), wait);

    // what we actually return is this function which will really just add the new args to
    // allArgs using the combine fn
    return (...args) => {
      allArgs = combine(allArgs,  [...args]);
      wrapper();
    };
  },
});

@kmannislands嘿,您的版本不会在wrapper() allArgs $ ,因此对func()的后续调用会获取历史批次的参数以及当前批次。

不应该是:

const wrapper = _.debounce(() => {
    const args = allArgs;
    allArgs = undefined;
    func(args);
}, wait);

@markjaquith +1

此外func( args )与使用func.apply(context, args);的原始版本不同。
对于前者, args在目标func()中按原样使用,而在后面的_(原始代码)_中,您需要在普通函数中使用arguments( ...args )在 es6 胖箭头函数中。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

xiaoliwang picture xiaoliwang  ·  3评论

acl0056 picture acl0056  ·  5评论

umarfarooq125 picture umarfarooq125  ·  8评论

arieljake picture arieljake  ·  4评论

clouddueling picture clouddueling  ·  3评论