1 min read

JavaScript debounce

页面有一个按钮,点击一次可以触发一个 ajax 请求。如果不做特殊处理,那么,快速点击时就会触发大量多余 ajax 请求。

处理方法有多种。

点击后禁用按钮

简单粗暴,在用户第一次点击后即禁用按钮。在处理完 ajax 请求后,再启用该按钮。

Debounce

维基上 debounce 译成去除抖動。我们可以把用户迅速点击造成的大量事件触发看成「信号的抖动」 – 这时信号还不稳定。

debounce 的原理是,事件触发时,事件处理器延迟执行(setTimeout),比如 100 毫秒,如果 100 毫秒内没有再触发事件,则可以认为信号稳定,事件处理器可以在预计的 100 毫秒【当然,这个时间并不准确,有兴趣可以看 JavaScript 异步一篇的说明】内执行,但如果 100 毫秒内,比如等到 99 毫秒,又进来一个同样的事件,则上一个事件处理器被取消,新的事件处理器替补进来,并且重新开始延迟计时。

举一个点击按钮提交 ajax 请求的例子

$('#fav').on('click',function () {
    var timer = null;
    return function () {
        clearTimeout(timer); // 新进入的事件处理器清除上一个定时的事件处理器
        timer = setTimeout(function () { // 启动新的延时
            $.ajax({...});
        }, 100);
    }
});

这个方法有一个明显的缺陷,就是用户正常点击时,事件的处理也被无例外地推迟 100 毫秒。

不过,underscore 等类库提供了另一种 debounce 方式:第一个事件发生时,马上执行我们要执行的函数,第二次函数要想执行,则必须在上一个事件发生 x 秒之后。

throttle

throttle 的定义是:

a device controlling the flow of fuel or power to an engine.

在这里,可以简单理解成一个控制函数发生频率的机制。

与 debounce 不同的是,throttle 方法不会延迟第一次事件的处理。它即时处理第一次事件,并记录时间,之后再发生事件,它再计算一个时间值,这个时间值表示,离第一次事件发生过去了多久,如果这个时间值小于设定的时间阀(threshold),则利用 setTimeout 推迟事件处理,一旦到达时间阀,则即时触发事件处理。

这样,throttle 就控制住密集事件可触发事件处理器的频率,比如每 200 秒仅触发一个事件函数。

扩展阅读

Debouncing and Throttling Explained Through Examples | CSS-Tricks

报告问题 修订

如果你有自建 https 代理的需求,欢迎尝试 Phantom,一键搭建,方便快捷。查看 demo