节流(throttle)
节流指的都是某个函数在一定时间间隔内只执行第一次回调。举个常见的节流案例:我们把某个表单的提交按钮——button 设成每三秒内最多执行一次 click 响应;当你首次点击后,函数会无视之后三秒的所有响应;三秒结束后,button 又恢复正常 click 响应功能,以此类推。
有什么用呢?通常,这类提交 button 的@click响应会给后端发送 api 请求,频繁的点击意味着频繁的请求(流量)——会给后端带来很大的压力;此外,这些回调请求返回后,往往会在前端响应其他事件(如刷新页面),可能导致页面不停的加载,影响用户体验。所以我们要给这个 button 添加节流函数,防止一些无意义的点击响应。
节流实现
节流的实现就要用到 js 高阶函数了。基础版 throttle 实现很简单:利用闭包记录前一次执行的时间戳,并判断本次点击和前一次点击的时间间隔,超过设定域值(如 3 秒)才响应函数,反之不响应:
代码语言:javascript复制const throttle = (cb, wait = 3000) =>{
let previous = 0;
return (...args) => {
const now = new Date();
if( now - previous > wait ){
previous = now;
cb.apply(this, args);
}
}
}
例如滚动加载,滚动到底部了,数据正在加载,用户重复触发滚动到底部,这时就需要节流,没加载完之前,不会触发第二次
这里的cb
就是被执行的回调函数,wait
是设定的时间间隔。具体使用的时候,只要给常规监听的回调函数套一层throttle方法即可:
$button.addEventListener("click", throttle(cabllback));
还有一种常见的实现是做个定时器锁,只是会延后执行首次响应事件(如 3 秒后再执行回调);不过,同样可以确保特定时间间隔内只执行一次响应。
代码语言:javascript复制const throttle = (cb, wait=3000) => {
let timeId;
return (...args) => {
if( timeId ) return;
timeId = setTimeout(() => {
cb.apply(this, args);
timeId = undefined;
}, wait)
}
}
防抖动(debounce)
所谓的抖动就是浏览器频繁布局时,由于算力不足导致的页面颤动现象。防抖动就是利用类似于节流的手段——无视短时间内重复回调,避免浏览器发生抖动现象的技术。限流和防抖动在设计思想上一脉相承,只是限流是在某段时间内只执行首次回调,而防抖动通常是只执行末次回调。
比较常见的抖动场景是在自动索引的搜索设计上;当我们在搜索框内输入不同索引时,页面会频繁计算索引并渲染列表,以致产生抖动。但事实上在这类场景里,有价值的请求只会发生在用户停止输入后,通俗来说就是用户输入过程中的字符串不必当真。
Debounce 就是用来过滤输入过程中无意义的响应。实现上,只需要设置一个定时器(setTimeout),并在定计时器启动后(如 3 秒后)执行这个回调函数;若在定时器启动前又有相同回调到来,便取消之前的定时器(clearTimeout)——之前的回调便取消了;然后再创一个新的定时器回调,如此反复。
代码语言:javascript复制function debounce(cb, wait = 3000) {
let timer = null
return (...args) => {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
timer = null;
cb.apply(this, args)
}, wait)
}
}
很多库在实现 debounce 的时候还喜欢加第三个参数——immediate
:一个 boolean 参数,表示是否执行首次响应(默认是最后一次)。这算是防抖动和节流结合使用的实现了:
function debounce(cb, wait = 3000, immediate = false) {
let timeout;
return function(...args) {
let callNow = immediate && !timeout;
if (timeout) clearTimeout(timeout);
timeout = setTimeout(() => {
timeout = null;
if (!immediate) cb.apply(this, args);
}, wait);
if (callNow) cb.apply(this, args);
};
}
实现上也不难,这里timeout表示 debounce 是否已被调用,它与immediate共同决定是否立即执行回调函数——callNow。若 immediate 被设成了 true 并且没有开启的计时器(!timeout),则能被callNow,便会立即执行 cb(不会在 setTimeout 里执行)。其他实现与普通 debounce 相同。
转自简书:https://www.jianshu.com/p/11b206794dca
总结
- 节流: 控制高频事件执行次数
- 防抖:用户触发事件过于频繁,只要最后一次事件的操作
函数防抖是某一段时间内只执行一次;而函数节流是间隔时间执行,不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数。
防抖是维护一个计时器,规定在delay时间后触发函数,但是在delay时间内再次触发的话,都会清除当前的 timer 然后重新设置超时调用,即重新计时。这样一来,只有最后一次操作能被触发。
节流是通过判断是否到达一定时间来触发函数,若没到规定时间则使用计时器延后,而下一次事件则会重新设定计时器。
demo:传入一个函数,返回的也是这个函数,调用这个函数三次后就不在调用
代码语言:javascript复制function transformer(func) {
let i = 3;
return function (...args) {
const context = this;
if (i > 0) {
i--;
return func.apply(context, args);
} else {
return;
}
}
}