[TOC] 防抖与节流
实际上防抖和节流都是限制一些频繁的事件触发(window 的 resize、scroll、mousedown、mousemove、keyup、keydown),但他们还是有实质性的区别的防抖是虽然事件持续触发,但只有等事件停止触发后n秒才执行函数(如果你在时间内重新触发事件,那么时间就重新算,这是防抖的特点,可以按照这个特点选择场景)。比如生活中的坐公交,就是一定时间内,如果有人陆续刷卡上车,司机就不会开车。只有别人没刷,司机才开车。
节流是持续触发的时候,每隔n秒执行一次函数比如人的眨眼睛,就是一定时间内眨一次。这是函数节流最形象的解释
防抖与节流
防抖和节流的作用都是防止函数多次调用
节流:将多次执行变成每隔一段时间执行
防抖:将多次执行变成最后一次执行
节流: 规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。防抖: 在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。
举个栗子
个人理解 函数防抖就是法师发技能的时候要读条,技能读条没完再按技能就会重新读条。
个人理解 函数节流就是fps游戏的射速,就算一直按着鼠标射击,也只会在规定射速内射出子弹。
code
- debounce
触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间
其实函数防抖的原理也非常地简单,通过闭包保存一个标记来保存 setTimeout 返回的值,每当用户输入的时候把前一个 setTimeout clear 掉,然后又创建一个新的 setTimeout,这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数了。
- 思路
每次触发事件时都取消之前的延时调用方法
//模拟一段ajax请求
function ajax(content) {
console.log('ajax request ' + content)
}
function debounce(fn, interval = 300) {
let timeout = null;
return function () {
clearTimeout(timeout);//把前一个 setTimeout clear然后产生一个新的定时器
timeout = setTimeout(() => {
fn.apply(this, arguments);
}, interval);
};
}
let inputb = document.getElementById('debounce')
let debounceAjax = debounce(ajax, 500)
inputb.addEventListener('keyup', function (e) {
debounceAjax(e.target.value)
})
- throttle
高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率
- 思路
每次触发事件时都判断当前是否有等待执行的延时函数
function throttle(fn) {
let canRun = true; // 通过闭包保存一个标记
return function () {
if (!canRun) return; // 在函数开头判断标记是否为true,不为true则return
canRun = false; // 立即设置为false
setTimeout(() => { // 将外部传入的函数的执行放在setTimeout中
fn.apply(this, arguments);
// 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。当定时器没有执行的时候标记永远是false,在开头被return掉
canRun = true;
}, 500);
};
}
function sayHi(e) {
console.log(e.target.innerWidth, e.target.innerHeight);
}
window.addEventListener('resize', throttle(sayHi));
异同比较
-
相同点
- 都可以通过使用 setTimeout 实现。
- 目的都是,降低回调执行频率。节省计算资源。
-
不同点
- 函数防抖,在一段连续操作结束后,处理回调,利用clearTimeout 和 setTimeout实现。函数节流,在一段连续操作中,每一段时间只执行一次,频率较高的事件中使用来提高性能。
函数防抖关注一定时间连续触发的事件只在最后执行一次,而函数节流侧重于一段时间内只执行一次。
总结
- 防抖判断机制是定时器id,有个重新计算的概念
- 节流是true/false开关有个周期的概念
- 函数防抖和函数节流都是防止某一时间频繁触发,但是这两兄弟之间的原理却不一样。
- 函数防抖是某一段时间内只执行一次,而函数节流是间隔时间执行。
应用场景
-
debounce
- search搜索联想,用户在不断输入值时,用防抖来节约请求资源。
- window触发resize的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次
-
throttle
- 鼠标不断点击触发,mousedown(单位时间内只触发一次)
- 监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断
30 seconds
const debounce = (fn, ms = 0) => {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn.apply(this, args), ms);
};
};
const throttle = (fn, wait) => {
let inThrottle, lastFn, lastTime;
return function() {
const context = this,
args = arguments;
if (!inThrottle) {
fn.apply(context, args);
lastTime = Date.now();
inThrottle = true;
} else {
clearTimeout(lastFn);
lastFn = setTimeout(function() {
if (Date.now() - lastTime >= wait) {
fn.apply(context, args);
lastTime = Date.now();
}
}, Math.max(wait - (Date.now() - lastTime), 0));
}
};
};