事件响应函数在一段时间后才会执行,如果在这段时间内再次调用,则重新计算执行时间;当预定时间内没有再次调用该函数,则执行响应逻辑。
可以理解为在函数防抖上多加了一个功能:函数节流会预定一个自动执行时间,到时自动执行一次。
throttle 和 debounce 是解决请求和响应速度不匹配问题的两个方案。二者的差异在于选择不同的策略。
如果还不明白,再举一个通俗的例子:
电梯超时
想象每天上班大厦底下的电梯。把电梯完成一次运送,类比为一次函数的执行和响应。假设电梯有两种运行策略 throttle 和 debounce ,超时设定为15秒,不考虑容量限制。
throttle 策略的电梯。保证如果电梯第一个人进来后,15秒后准时运送一次,不等待。如果没有人,则待机。
debounce 策略的电梯。如果电梯里有人进来,等待15秒。如果又人进来,15秒等待重新计时,直到15秒超时,开始运送。
共同点:
函数节流与函数防抖都是为了限制函数的执行频次,是一种性能优化的方案,比如应用于window对象的resize、scroll事件,拖拽时的mousemove事件,文字输入、自动完成的keyup事件。
举例 可看 带图详解
以百度搜索为例:用户搜索输入时,边输入会边提示搜索内容
从网络请求来看,百度是监听了键盘输入,按一次键就发一次请求
对于百度来说,这样自然没什么问题,毕竟服务器强大,这点消耗不算什么,但在我们看来,有什么可以优化的呢?
方案一:在用户没有输入的m毫秒后再发送请求(函数去抖)
方案二:在用户没有输入的m毫秒后再发送请求,但期间每隔n毫秒会自动发一次请求(函数节流)
函数去抖实现
实现用户停止输入1秒后开始发送搜索请求,以下实现不注重样式,仅实现需求。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>函数去抖</title>
</head>
<body>
<h4>函数去抖实现</h4>
<input type="text">
<div class="show"></div>
</body>
<script>
var input = document.getElementsByTagName('input')[0]
input.addEventListener('keyup', function () {
debounce(doSearch, window, this.value)
})
function doSearch (searchText) {
var p = document.createElement('p')
p.innerText = '当前搜索内容: ' + searchText
document.getElementsByClassName('show')[0].appendChild(p)
}
function debounce (fn, context, searchText) {
clearTimeout(this.timer) // 关键点:1000ms内重复调用则清除timer
this.timer = setTimeout(function() {
fn.call(context, searchText) // 调用函数fn,context为fn所属作用域
}, 1000)
}
</script>
</html>
函数节流实现
以下实现用户停止输入2秒后发搜索请求,期间每隔500ms发一次搜索请求。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>函数节流</title>
</head>
<body>
<h4>函数节流实现</h4>
<input type="text">
<div class="show"></div>
</body>
<script>
var input = document.getElementsByTagName('input')[0]
input.addEventListener('keyup', function() {
throttle(doSearch, window, this.value, 500, 2000)
})
function doSearch (searchText) {
var p = document.createElement('p')
p.innerText = '当前搜索内容: ' + searchText
document.getElementsByClassName('show')[0].appendChild(p)
}
// (调用方法, 方法所属对象, 自动执行时间, 最大延迟执行时间 ))
function throttle (fn, context, searchText, autoDoTime, delay) {
clearTimeout(fn.timer)
// 记录当前时间
fn.currentTime = Date.now()
// 若该函数是第一次调用,则直接设置startTime,即开始时间,为currentTime,即此刻的时间
if(!fn.startTime){
fn.startTime = fn.currentTime
}
// 大于等于自动执行时间,调用方法
if(fn.currentTime - fn.startTime >= autoDoTime){
fn.call(context, searchText)
fn.startTime = fn.currentTime
} else {
// 大于最大延迟时间,调用方法
fn.timer = setTimeout(function(){
fn.call(context, searchText)
}, delay)
}
}
</script>
</html>
只要牵涉到连续事件或频率控制相关的应用都可以考虑到这两个函数,比如:
input 中输入文字自动发送 ajax 请求进行自动补全: debounce
resize window 重新计算样式或布局:debounce
keydown 事件、文本输入、自动完成,keyup 事件:debounce或者throttle 按需引入
鼠标移动,mousemove 事件、DOM 元素动态定位,window 对象的 resize 和 scroll 事件:throttle