节流 (throttle) 和消抖 (debounce)
简介
背景 : 浏览器的一些事件,如:resize,scroll,keydown,keyup,keypress,mousemove 等。这些事件触发频
率太过频繁,绑定在这些事件上的回调函数会不停的被调用。这样浏览器的目的是为了保证信息的一致
性,而对于我们来说就是一种资源的浪费了
作用 : 在控制函数多次调用的时候,压缩调用的次数,减少不必要的资源消耗,提高性能
节流 throttle : 函数立即执行并在规定的时间内不允许再次执行。规定时间内多次执行只执行第一次
的操作。类似于我们过安检的时候,一次进入几个人进去安检,其他人在外等待。直到这批人安检完成
之后再让一批人进入
消抖 debounce : 函数延时执行,延时的时间内如果再次执行了这个函数则取消之前的延时重新计算延时
时间。规定时间内多次执行只执行最后一次操作。类似于我们做电梯,在一定时间内没有人进入电梯的
时候电梯就会运行,当有人点击了电梯外部的的按钮时候,电梯则会重新计算这个等待的时间
应用场景 :
例如 DOM 的拖拽,如果用消抖的话,就会出现卡顿的感觉,因为只会在停止的时候执行了一
次。这个时候就应该用节流,在一定时间内多次执行,会流畅很多
如果是输入联想这种,我要输入 “谁是世界上最帅的人?”,多次执行的话,可能当我输到最的时
候,就去搜索,结果搜出来一堆“谁是世界上最笨”,“谁是世界上最胖”之类的不必要搜索,只需要
在输入完成后进行搜索,消抖当时最合适
实现
大牛的实现 Underscore.js (1.9.1)
_.throttle = function(func, wait, options) {
var timeout, context, args, result
var previous = 0
if (!options) options = {}
var later = function() {
previous = options.leading === false ? 0 : _.now()
timeout = null
result = func.apply(context, args)
if (!timeout) context = args = null
}
var throttled = function() {
var now = _.now()
if (!previous && options.leading === false) previous = now
var remaining = wait - (now - previous)
context = this
args = arguments
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout)
timeout = null
}
previous = now
result = func.apply(context, args)
if (!timeout) context = args = null
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining)
}
return result
}
throttled.cancel = function() {
clearTimeout(timeout)
previous = 0
timeout = context = args = null
}
return throttled
}
_.debounce = function(func, wait, immediate) {
var timeout, result
var later = function(context, args) {
timeout = null
if (args) result = func.apply(context, args)
}
var debounced = restArguments(function(args) {
if (timeout) clearTimeout(timeout)
if (immediate) {
var callNow = !timeout
timeout = setTimeout(later, wait)
if (callNow) result = func.apply(this, args)
} else {
timeout = _.delay(later, wait, this, args)
}
return result
})
debounced.cancel = function() {
clearTimeout(timeout)
timeout = null
}
return debounced
}
_.debounce = function(func, wait) {
var timeout
return function() {
if (timeout) {
clearTimeout(timeout)
timeout = null
}
timeout = setTimeout(func, wait)
}
}
_.throttle = function(func, wait) {
var previous = -1
return function() {
var now = new Date().getTime()
if (previous === -1 || previous + wait >= now) {
func.apply(this, arguments)
previous = now
}
}
}