dart 防抖和节流
前端的开发者都或多或少的遇到过节流与防抖的问题。函数节流和函数防抖,两者都是优化执行代码效率的一种手段。在一定时间内,代码执行的次数不一定是越多越好。相反,频繁的触发或者执行代码,会造成大量的重绘等问题,影响浏览器或者机器资源。因此把代码的执行次数控制在合理的范围。既能节省浏览器 CPU 资源,又能让页面浏览更加顺畅,不会因为 js 的执行而发生卡顿。这就是函数节流和函数防抖要做的事。
flutter 也同样有此场景。
防抖(debounce)
防抖在生活中随处可见,毕竟经典的就是电梯,当没人进出电梯的时候,过一段时间它就会自动关上,而如果在它关上之前有人按了开门或者进出电梯,那么电梯关门时间又会重新计时。
在代码里,触发事件时,不立即执行目标操作,而是给出一个延迟的时间,在该时间范围内如果再次触发了事件,则重置延迟时间,直到延迟时间结束才会执行目标操作。
示例
如设定延迟时间为 500ms,
如果在 500ms 内没有再次触发事件,则执行目标操作
如果在 500ms 内再次触发了事件,则重置延迟时间,重新开始 500ms 的延迟,直到 500ms 结束执行目标操作
效果
连续点击防抖按钮,停止点击时才会执行”debounceCount++”操作,只会执行一次操作
示例图
代码
TextButton.icon(
icon: Icon(Icons.add),
label: Text('防抖:$debounceCount'),
onPressed: debounce(() {
if (!mounted) {
return;
}
setState(() {
debounceCount++;
});
}),
);
/// 函数防抖
///
/// [func]: 要执行的方法
/// [delay]: 要迟延的时长
void Function() debounce(
Function func, [
Duration delay = const Duration(milliseconds: 500),
]) {
Timer? timer;
void Function() target = () {
timer?.cancel();
timer = Timer(delay, () {
func.call();
});
};
return target;
}
/// 也可以采用类的方式
class Debouncer {
final Duration? delay;
Timer? _timer;
Debouncer({this.delay});
void call(void Function() action) {
_timer?.cancel();
_timer = Timer(delay ?? const Duration(milliseconds: 1000), action);
}
}
节流(throttle)
在触发事件时,立即执行目标操作,同时给出一个延迟的时间【也有说是先给出延迟时间再执行的】,在该时间范围内如果再次触发了事件,该次事件会被忽略,直到超过该时间范围后触发事件才会被处理。
示例
如设定延迟时间为 500ms,
如果在 500ms 内再次触发事件,该事件会被忽略
如果 500ms 延迟结束,则事件不会被忽略,触发事件会立即执行目标操作,并再次开启 500ms 延迟
效果
连续点击防抖按钮,在本次操作执行完成前的多次点击会被忽略,只会执行一次”throttleCount++”操作。
TextButton.icon(
icon: Icon(Icons.add),
label: Text('节流:$throttleCount'),
onPressed: throttle(() {
if (!mounted) {
return;
}
setState(() {
throttleCount++;
});
}) as void Function()?,
);
/// 节流
///
/// [func]: 要执行的方法
/// [delay]: 要迟延的时长
Function throttle(
Function func, [
Duration delay = const Duration(milliseconds: 500),
]) {
Timer? timer;
return () {
if (timer != null) return;
timer = Timer(delay, () {
func.call();
});
};
}
class Throttle {
final Duration delay;
Timer? timer;
Throttle({this.delay = const Duration(milliseconds: 500)});
void call(Function callBack) {
if (timer != null) return;
timer = Timer(delay, () {
callBack.call();
});
}
}