不从概念触发,先从应用讲起。如果要你设计一个输入搜索组件,你会怎么做。可能你觉得很简单,响应输入框的 keydown 和 change 事件,然后向服务器发起请求就可以了。这样的处理方案思路上是没毛病的,不过会存在两个问题,分别影响客户端和服务器。
- 过于频繁的响应事件,会降低用户体验。
- 过于频繁的请求服务器,增加服务器压力。
我们先思考一下用户增加、删除一个字符就发起请求的合理性。一般而言,搜索单个字符对于用户来说是没有意义的,用户往往是需要搜索一个单词或者短语。那么使用以上的设计,在用户输入他想要搜索的文字之前的所有搜索,都是浪费的请求。同时,过于频繁的处理事件,当事件响应变多了之后,页面会出现卡顿也是理所当然的事。另外,对于服务器而言,搜索一般来说本身就是比较复杂、耗时的服务,过多的请求不可避免地增加服务器的并发量更是给服务器增加压力。
从以上的简单分析可以知道,我们并不需要对用户的输入进行立马响应,可以等待用户输入停止之后再进行实际的逻辑处理,这样的设计也是更符合用户体验的。
这样的想法就是所谓的防抖
,而另一个相似的做法是节流
。他们的区别是,防抖是在最后一次事件触发后等待一定的时间再做逻辑处理,如果在这个等待时间结束之前有新的事件发生,那么这个事件就是最后一次事件,逻辑处理的时机也要相应往后延;而节流是每隔一定时间就做一次逻辑处理。他们的共同点都是降低逻辑处理的频率。
简单的防抖代码如下
function debounce(func, timespan) {
let timer;
return function () {
const context = this;
const args = arguments;
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(context, args);
}, timespan);
};
}
简单的节流代码如下
function throttle(func, timespan) {
let running = false;
let context;
let args;
return function () {
context = this;
args = arguments;
if (!running) {
running = true;
setTimeout(() => {
func.apply(context, args);
running = false;
}, timespan);
}
};
}
一张图来对比一下他们在时序上的区别(上边是 debounce,下边是 throttle,每个框等时长)