下划线的防抖函数与纯JavaScript的setTimeout有何不同?

24
我理解Underscore.js中的debounce会返回一个函数,该函数将推迟其执行直到等待时间结束。
我的问题是,在普通的JavaScript中,使用debounce是否比使用常规的setTimeout函数有优势? 它们不是都可以起到同样的作用吗?

1
它们之间有着显著的不同。我建议你重新阅读文档:http://underscorejs.org/#debounce(并注意[debounce在底层使用了setTimeout](https://github.com/jashkenas/underscore/blob/012fe2b77e3d206d763c0c078d3b97a63639fc24/underscore.js#L855)) - Quentin
5个回答

19

它们非常不同,用途完全不同。

  1. _.debounce返回一个functionsetTimeout返回一个id,您可以使用该ID取消timeOut。

  2. 无论您调用由_.debounce返回的函数多少次,在给定的时间范围内它都只会运行一次。

var log_once = _.debounce(log, 5000);

function log() {
  console.log('prints');
}

log_once();
log_once();
log_once();
log_once();
log_once();

var id = setTimeout(function() {
  console.log('hello');
}, 3000);
clearTimeout(id);
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js"></script>


2
只是提供信息,这个Vanilla debounce方法返回Timeout,因此如果您选择,可以手动取消它。 - SpYk3HH

12
setTimeout和debounce根本不是同一回事。setTimeout只是等待n毫秒,然后调用提供的函数。debounce返回一个函数,只有在上次调用该函数之后的n毫秒才调用回调函数。

差别很大。防抖/节流(它们不是同一回事)函数经常用于减少用户输入导致的函数调用量。想象一下自动完成/类型提示字段。您可能会在每次按键时进行ajax请求,但这可能会变得很重,因此您可以将函数防抖,以便它仅在上一个按键之后的200毫秒触发。

您可以在此处查看文档:https://lodash.com/docs#debounce


2
除非您的事件处理程序先取消超时,然后重新设置它。那么,您就可以获得相同的行为。 - Mike Christian
你可以使用setTimeout和clearTimeout来实现防抖功能。 你将setTimeout赋值给一个变量,并在变量声明之前使用clearTimeout。let timer;function SetTimer(){ clearTimeout(timer); // 如果timer已经被赋值,clearTimeout将起作用 timer = setTimeout(() => console.log("here"), 1000); }; - undefined

9

你也可以在Vanilla JavaScript中实现自己的防抖函数。一篇被广泛引用的文章是David Walsh关于使用underscore进行函数防抖的文章,其中包括underscore在他们的实现中使用的源代码:

// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
function debounce(func, wait, immediate) {
    var timeout;
    return function() {
        var context = this, args = arguments;
        var later = function() {
            timeout = null;
            if (!immediate) func.apply(context, args);
        };
        var callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func.apply(context, args);
    };
};

Debounce函数用作你想要调用的实际函数的生成器,这样状态就可以在闭包内进行持久化,就像这样:

// example function
let sayHello = (name) => console.log(`Hi ${name}`)

// generate a debounced version with a min time between calls of 2 seconds
let sayHelloDebounced = debounce(sayHello, 2000)

// call however you want
sayHelloDebounced('David')

在 Stack Snippets 中的演示

function debounce(func, wait, immediate) {
 var timeout;
 return function() {
  var context = this, args = arguments;
  var later = function() {
   timeout = null;
   if (!immediate) func.apply(context, args);
  };
  var callNow = immediate && !timeout;
  clearTimeout(timeout);
  timeout = setTimeout(later, wait);
  if (callNow) func.apply(context, args);
 };
};

let sayHello = (name) => console.log(`Hi ${name}`)

let sayHelloDebounced = debounce(sayHello, 2000)

sayHelloDebounced('David')
sayHelloDebounced('David')
sayHelloDebounced('David')

其他实现方式

这里提供了一些其他的防抖实现方式,你可以参考它们的文档和源代码。包括Underscore、Lodash和30秒代码等库或资源。

0
setTimeout会在n秒后运行一次。所以如果你调用setTimeout 4次,它会运行4次。而debounce只会被调用一次。
在正式的英语中,debounce所做的事情相当于setTimeout和clearTimeout的结合。 如果你不想使用debounce,你可以使用setTimeout和clearTimeout一起来实现相同的行为。
let timer; 
function setTimer(){ 
  clearTimeout(timer); 
  timer = setTimeout(() => console.log("here"), 1000); 
};
setTimer();
setTimer();
setTimer();
setTimer();
// here

enter image description here


-1

我看到了一篇博客文章,对防抖和节流有更清晰的解释。如果上面的答案让你感到困惑,请务必查看它。它帮助我消除了疑虑。 Javascript中的防抖和节流


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