demo 地址: https://github.com/iotjin/jh_flutter_demo
代码不定时更新,请前往github查看最新代码
什么是防抖和节流?
函数节流(throttle)与 函数防抖(debounce)都是为了限制函数的执行频次,以优化函数触发频率过高导致的响应速度跟不上触发频率,出现延迟,假死或卡顿的现象
是应对频繁触发事件的优化方案。
防抖(debounce)
防抖就是防止抖动,避免事件的重复触发。
防抖可以概括为触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间。
n 秒后执行该事件,若在n秒后被重复触发,则重新计时
简单的说,如果某一事件被连续快速地触发多次,只会执行最后那一次。
- 实现方式:每次触发事件时设置一个延迟调用方法,并且取消之前的延时调用方法
- 如果事件在规定的时间间隔内被不断的触发,则调用方法会被不断的延迟
使用场景
- input 搜索录入,用户不断录入值
- window触发resize事件
- mousemove 鼠标滑动事件
- scroll滚动条滑动请求、上拉触底加载更多
- 表单验证,按钮的提交事件
节流(throttle)
节流就是减少流量,将频繁触发的事件减少,并每隔一段时间执行。会控制事件触发的频率。所以节流会稀释函数的执行频率。
n 秒内只运行一次,若在n秒内重复触发,只有一次生效。
如果连续快速地触发多次,在规定的时间内,只触发一次。如限制1s,则1s内只执行一次,无论怎样,都在会1s内执行相应的操作。
- 实现方式:每次触发事件时,如果当前有等待执行的延时函数,则直接return
- 如果事件在规定的时间间隔内被不断的触发,则调用方法在规定时间内都会执行一次
使用场景
- 获取验证码
- 鼠标不断点击触发,mousedown(规定时间内只触发一次)
- mousemove 鼠标滑动事件
- 滚动条滑动请求、上拉触底加载更多
- 搜索、提交等按钮功能
防抖和节流之间的差别:
相同点:
- 都可以通过使用 setTimeout 实现
- 目的都是,降低回调函数的执行频率,节省计算资源
不同点:
-
防抖,是在一段连续操作结束之后,处理回调,利用 clearTimout 和 setTimeout 实现。
-
节流,是在一段连续操作中,每一段时间只执行一次,在频率较高的事件中使用来提高性能。
-
防抖用于无法预知的用户主动行为,如用户输入内容去服务端动态搜索结果。用户打字的速度等是无法预知的,具有非规律性。
-
节流可用于一些非用户主动行为或者可预知的用户主动行为,如用户滑动商品橱窗时发送埋点请求、滑动固定的高度是已知的逻辑,具有规律性。
-
防抖是关注于最后一次的事件触发,而节流则是在规定的时间里只执行一次。
-
防抖是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行
防抖和节流 utils
class JhCommonUtils {
static Timer? _debounceTimer;
/// 防抖 (传入所要防抖的方法/回调与延迟时间)
static void debounce(Function func, [int delay = 500]) {
if (_debounceTimer != null) {
_debounceTimer?.cancel();
}
_debounceTimer = Timer(Duration(milliseconds: delay), () {
func.call();
_debounceTimer = null;
});
}
/// 防抖 (传入所要防抖的方法/回调与延迟时间)
static debounce2(Function func, [int delay = 500]) {
Timer? timer;
return () {
if (timer != null) {
timer?.cancel();
}
timer = Timer(Duration(milliseconds: delay), () {
func.call();
timer = null;
});
};
}
/// 录入框防抖 (传入所要防抖的方法/回调与延迟时间)
static debounceInput(Function(dynamic) func, [int delay = 500]) {
Timer? timer;
return (dynamic value) {
if (timer != null) {
timer?.cancel();
}
timer = Timer(Duration(milliseconds: delay), () {
func.call(value);
timer = null;
});
};
}
static Timer? _throttleTimer;
static bool _throttleFlag = true;
/// 节流 (传入所要节流的方法/回调与延迟时间)
static void throttle(Function func, [int delay = 500]) {
if (_throttleFlag) {
func.call();
_throttleFlag = false;
return;
}
if (_throttleTimer != null) {
return;
}
_throttleTimer = Timer(Duration(milliseconds: delay), () {
func.call();
_throttleTimer = null;
});
}
/// 节流 (传入所要节流的方法/回调与延迟时间)
static throttle2(Function func, [int delay = 500]) {
Timer? timer;
bool firstTime = true;
return () {
if (firstTime) {
func.call();
firstTime = false;
return;
}
if (timer != null) {
return;
}
timer = Timer(Duration(milliseconds: delay), () {
func.call();
timer = null;
});
};
}
/// 节流 (传入所要节流的方法/回调与延迟时间)
static throttle3(Function func, [int delay = 500]) {
Timer? timer;
bool isExecuting = false;
return () {
if (isExecuting) return;
isExecuting = true;
timer?.cancel();
timer = Timer(Duration(milliseconds: delay), () {
func.call();
isExecuting = false;
});
};
}
}
未处理时,录入文字改变就会触发一次事件,或者点击一次就会触发一次事件
防抖处理后
录入文字使用的防抖,设置的1.0秒间隔,一直录入只在最后一次录入结束触发
按钮防抖同样,一直点击只在最后一次点击触发
节流处理后
点击按钮使用节流,设置的2秒间隔,点击多次2秒内只会触发一次
用法(关键代码):
一种是在回调函数内部调用工具类,一种是在某个回调函数上使用
// 防抖 用法一
_input1() {
return JhFormInputCell(
title: '防抖1:',
text: _keyWord1,
hintText: '请输入',
maxLength: 140,
textInputAction: TextInputAction.search,
inputCallBack: (value) {
print('防抖1 未处理1 _keyWord1 : $value');
JhCommonUtils.debounce(() => _inputCallBack1(value, false), 500);
},
inputCompletionCallBack: (value, isSubmitted) {
print('防抖1 未处理2 _keyWord1 : $value');
if (isSubmitted) {
print('防抖1 未处理3 _keyWord1 : $value');
JhCommonUtils.debounce(() => _inputCallBack1(value, true), 500);
}
},
);
}
// 防抖 用法二
_input2() {
return JhFormInputCell(
title: '防抖2:',
text: _keyWord2,
hintText: '请输入',
maxLength: 140,
textInputAction: TextInputAction.search,
inputCallBack: JhCommonUtils.debounceInput((value) {
print('防抖后 防抖2 _keyWord2 : $value');
JhProgressHUD.showText('防抖2 _keyWord2 : $value');
setState(() {
_keyWord2 = value;
// 其他操作
});
}, 500),
inputCompletionCallBack: (value, isSubmitted) {
print('防抖2 未处理2 _keyWord2 : $value');
if (isSubmitted) {
print('防抖2 未处理3 _keyWord2 : $value');
JhCommonUtils.debounce(() => _inputCallBack2(value, true), 500);
}
},
);
}
_inputCallBack1(value, isSubmitted) {
print('防抖后 防抖1 _keyWord1 : $value');
JhProgressHUD.showText('防抖1 _keyWord1 : $value');
setState(() {
_keyWord1 = value;
// 其他操作
});
}
_inputCallBack2(value, isSubmitted) {
print('防抖后 防抖2 _keyWord2 : $value');
JhProgressHUD.showText('防抖2 _keyWord2 : $value');
setState(() {
_keyWord2 = value;
// 其他操作
});
}
// 节流 用法一
JhButton(
text: '按钮-节流1',
onPressed: () {
print('点击按钮-节流1');
JhCommonUtils.throttle(() => _clickThrottleBtn1(), 2000);
},
),
// 节流 用法二
JhButton(
text: '按钮-节流2',
onPressed: JhCommonUtils.throttle2(() {
print('节流后 - 点击按钮-节流2');
JhProgressHUD.showText('点击按钮-节流2,2000ms');
}, 2000),
),
_clickThrottleBtn1() {
print('节流后 - 点击按钮-节流1');
JhProgressHUD.showText('点击按钮-节流1,2000ms');
}