原文:Debounce – How to Delay a Function in JavaScript (JS ES6 Example),作者:Ondrej Polesny

在 JavaScript 中,防抖(debounce)函数可以确保你的代码在每个用户输入时只被触发一次。搜索框建议、文本字段自动保存和消除双键点击都是防抖的用例。

在本教程中,我们将学习如何在 JavaScript 中创建一个防抖函数。

什么是防抖?

防抖这个词来自于电子学。当你按下一个按钮时,比如说在你的电视遥控器上,信号很快就传到了遥控器的微芯片上,在你释放按钮之前,它就反弹了,而微芯片会多次记录你的“点击”。

debounce-button

为了缓解这种情况,一旦收到来自按钮的信号,微芯片就会在几微秒内停止处理来自按钮的信号,而你在物理上不可能再次按下按钮。

JavaScript 中的防抖

在 JavaScript 中,用例是类似的。我们想触发一个函数,但每个用例只触发一次。

比方说,我们想显示一个搜索查询的建议,但只有在访问者完成输入之后。

或者我们想保存表单上的修改,但只在用户没有自己处理这些修改的时候,因为每一次 “保存”都会让数据库运行一次。

我最喜欢的是——有些人真的习惯了 Windows 95,现在双击一切。

这是一个简单的防抖函数的实现(CodePen 示例):

function debounce(func, timeout = 300){
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => { func.apply(this, args); }, timeout);
  };
}
function saveInput(){
  console.log('Saving data');
}
const processChange = debounce(() => saveInput());

它可以用于 input:

<input type="text" onkeyup="processChange()" />

或者一个按钮:

<button onclick="processChange()">Click me</button>

或者一个窗口事件:

window.addEventListener("scroll", processChange);

而在其他元素上,它的实现就像一个简单的 JS 函数。

那么这里发生了什么?debounce 是一个特殊的函数,它处理两个任务:

  • 为定时器 timer 变量分配一个范围
  • 安排你的函数在一个特定的时间被触发

让我们解释一下在第一个文本输入的使用案例中是如何工作的。

当访问者写下第一个字母并释放按键时,debounce 首先用 clearTimeout(timer) 重置定时器。在这一点上,这个步骤是没有必要的,因为还没有安排什么。然后,它设置函数 saveInput() 在 300 毫秒内被调用。

但是,假设访问者一直在写,所以每次按键释放都会再次触发 debounce。每次调用都需要重置定时器,或者换句话说,用 saveInput() 取消之前的计划,并将其重新安排在未来 300 毫秒的新时间。只要访问者一直在 300 毫秒内按下按键,这个过程就会持续下去。

最后的计划不会被清除,所以 saveInput() 最后会被调用。

另一种方式——如何忽略后续事件

这对触发自动保存或显示建议很好。但如果是多次点击一个按钮的用例呢?我们不想等待最后一次点击,而是注册第一次点击,忽略其他的点击(CodePen 示例)。

function debounce_leading(func, timeout = 300){
  let timer;
  return (...args) => {
    if (!timer) {
      func.apply(this, args);
    }
    clearTimeout(timer);
    timer = setTimeout(() => {
      timer = undefined;
    }, timeout);
  };
}

这里我们在第一次点击按钮引起的第一次 debounce_leading 调用时触发 saveInput() 函数。我们把定时器的破坏时间安排在 300 毫秒。在这个时间范围内的每一个后续的按钮点击都已经定义了定时器,并且只会将销毁时间推到 300 毫秒以后。

库中的防抖实现

在这篇文章中,我向你展示了如何在 JavaScript 中实现函数防抖,并使用它来防抖网站元素触发的事件。

然而,如果你不想的话,你不需要在你的项目中自己去实现。广泛使用的 JS 库已经包含了防抖实现。这里有几个例子:jQuery (via library)$.debounce(300, saveInput);Lodash_.debounce(saveInput, 300);Underscore_.debounce(saveInput, 300);