节流与防抖
约 434 字大约 1 分钟
2024-08-14
防抖(Debounce)
防抖是将多次执行合并为最后一次执行,适用于只需最终值的场景。
原理
触发 → 等待 → 触发 → 等待 → ... → 执行(最后一次触发后)实现
function debounce(fn, delay, immediate = false) {
let timer = null;
return function (...args) {
const context = this;
if (timer) clearTimeout(timer);
if (immediate && !timer) {
fn.apply(context, args);
}
timer = setTimeout(() => {
if (!immediate) fn.apply(context, args);
timer = null;
}, delay);
};
}使用场景
- 搜索框输入(用户停止输入后才搜索)
- 窗口 resize(调整完成后执行)
- 表单验证
// 示例:搜索框
const handleSearch = debounce((value) => {
fetch(`/api/search?q=${value}`);
}, 300);
input.addEventListener('input', (e) => handleSearch(e.target.value));节流(Throttle)
节流是限制执行频率,保证每隔一段时间至少执行一次。
原理
触发 → 执行 → 冷却中 → 触发(忽略)→ 冷却中 → 执行 → ...实现
function throttle(fn, interval, options = {}) {
const { leading = true, trailing = true } = options;
let lastTime = 0;
let timer = null;
return function (...args) {
const context = this;
const now = Date.now();
if (!lastTime && !leading) lastTime = now;
const remaining = interval - (now - lastTime);
if (remaining <= 0 || remaining > interval) {
if (timer) {
clearTimeout(timer);
timer = null;
}
lastTime = now;
fn.apply(context, args);
} else if (!timer && trailing) {
timer = setTimeout(() => {
lastTime = leading ? Date.now() : 0;
timer = null;
fn.apply(context, args);
}, remaining);
}
};
}使用场景
- 滚动事件(滚动时定期更新)
- 按钮防重复点击
- 游戏中的射击
// 示例:滚动加载
const handleScroll = throttle(() => {
const { scrollTop, scrollHeight, clientHeight } = document.documentElement;
if (scrollTop + clientHeight >= scrollHeight - 100) {
loadMoreData();
}
}, 200);
window.addEventListener('scroll', handleScroll);对比
| 特性 | 防抖 | 节流 |
|---|---|---|
| 执行时机 | 最后一次触发后 | 固定间隔 |
| 适用场景 | 最终值、停止后触发 | 定期执行、冷却 |
| 搜索框 | ✅ | ❌ |
| 滚动加载 | ❌ | ✅ |
| resize | ✅(完成时) | ✅(进行中) |
Lodash 实现
import { debounce, throttle } from 'lodash';
// 防抖
const debouncedFn = debounce(fn, 300, { leading: false, trailing: true });
// 节流
const throttledFn = throttle(fn, 200, { leading: true, trailing: false });参考文章:
