超级面板
文章目录
最新文章
最近更新
文章分类
标签列表
文章归档

Debounce 和 Throttle

在一些应用场合下,由于事件被频繁的触发,导致为事件绑定的函数被频繁的调用,因为斌烦的执行大量无意义的DOM操作、Ajax请求等行为,造成页面的抖动和资源的大量消耗甚至错误的数据加载。常见的导致这些情况出现的事件主要这些:

  • window 对象的 resizescroll 事件
  • 鼠标拖拽的 mousemove 事件
  • 输入文本时的绑定的用于进行自动完成或自动加载的操作的 keyup 事件

对于这些情况通常可以延迟函数的执行时机,来进行优化,根据延迟的策略不同,有 debouncethrottle 两种解决办法:

Debounce

Debounce 就是强制一个函数在某个动作结束 n 毫秒后执行,如果在这 n 毫秒内,该动作再次发生,则重新计时。

下面是一个简单的实现:

/**
* @param func {Function} 目标函数
* @param delay {Number} 空闲时间,单位毫秒
* @param context {Object} 可省略,function 内部的上下文
* @return {Function} 返回客户调用函数
*/
var debounce = function (func, delay, context) {
var timer;
return function () {
var ctx = context || this;

//每次被调用就清除上次的定时器
//并新建一个定时器,重新计时
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function () {
func.apply(ctx, arguments);
}, delay);
};
};

// 使用:
window.addEventListener('resize', debounce(function (){
console.log('resize');
}, 250));

Throttle

Throttle 为函数设定一个执行周期,强制函数在指定的执行周期内执行且仅执行一次,也就是说,当动作发生时,如果该函数上次执行的时间到现在小于这个执行周期,则不执行函数。

下面是一个简单的实现:

/**
* @param func {Function} 目标函数
* @param threshhold {Number} 间隔时间,单位毫秒
* @param context {Object} 可省略,function 内部的上下文
* @return {Function} 返回客户调用函数
*/
var throttle = function (func, threshhold, context) {
var last;
var timer;

return function () {
var current = new Date();
var ctx = context || this;

// 在函数刚开始或到达指定间隔后执行func
if (!last || current - last > threshhold) {
last = current;
func.apply(ctx, arguments);

// 保证最终状态时候的调用能够被执行,否则最终状态可能会被过滤掉
} else {
clearTimeout(timer);

setTimeout(function() {
last = current;
func.apply(ctx, arguments);
})
}
};
};

window.addEventListener('resize', throttle(function () {
console.log('resize')
}, 250));

总结

这两个函数很相似,使用场景也很类似,最主要的就是区分两者对调用时机的处理,简单来说就是当函数被连续调用时 debounce 限制目标函数仅在停止调用时间足够长后才执行, throttle 限制目标函数以固定的速率执行,主要应用场景如下:

  • 表单根据输入自动补全或自动加载时使用 debounce
  • 处理 DOM 元素动态定位 resize、scroll 事件使用 throttle
  • 处理 mousemove 事件使用 throttle

当然这也并非是固定的,具体情况还要跟使用场景相关。

参考