防抖和节流
程序员文章站
2022-05-10 15:01:56
...
工作场景
- 用户在搜索的时候,如果每敲一个字就调用一次接口,接口调用频繁容易卡住
- 监听滚动,监听用户滚动的位置,如果每滚动一次就监听,操作频繁占内存
- 表单提交
防抖
在任务频繁触发的情况下,只有任务触发的间隔超过指定间隔的时候,任务才会执行。
例如:搜索框,输入之后会调用接口,获取联想词;为避免频繁调用接口,在代码中使用防抖,在用户输入完毕的一段时间后,才会调用接口
节流
指定的时间间隔只执行一次,
例如:点击提交按钮,我们知道接口大致的返回时间,使用节流,只允许规定的时间内点击一次,
实现防抖
// <button id="btn" >点击防抖</button>
function debounce(fn,wait){
var timerId = null;
var immediate = true;
return function(){
clearTimeout(timerId);
if (immediate) {
fn.call(this,arguments);
immediate = false
}
timerId = setTimeout(() => { immediate = true},wait)
}
}
// 节流事件
const func = debounce_2((e) => {
console.log('防抖');
}, 1000)
const btn = document.getElementById('btn')
btn.addEventListener('click', func)
实现节流
// <button id="throttle">点我节流!</button>
var myThrottle = document.getElementById("throttle");
myThrottle.addEventListener("click", throttle(this.sayThrottle));
// 节流函数体——闭包标记符号版
function throttle(fn, delay = 2000) {
// 通过闭包保存执行标记和立即执行标记
let canRun = true;
var immediate = true;
return function() {
// 在函数开头判断标志是否为 true,不为 true 则中断函数
if (!canRun) {
return;
}
// 判断是否立即执行
if (immediate) {
fn.call(this, arguments);
immediate = false;
} else {
// 将 canRun 设置为 false,防止执行之前再被执行
canRun = false;
// 定时器
setTimeout(() => {
fn.apply(this, arguments);
// 执行完事件(比如调用完接口)之后,重新将这个标志设置为 true
canRun = true;
immediate = true;
}, delay);
}
};
}
// 节流函数体——闭包标记+时间戳 来源:https://juejin.im/post/5c6bab91f265da2dd94c9f9e#heading-12
function throttle2(fun, delay = 2000) {
let timer = null;
let previous = 0;
return function(args) {
let now = Date.now();
let remaining = delay - (now - previous); // 操作间隔时间距离规定时间,还剩多少久
let that = this;
let _args = args;
clearTimeout(timer); // 清除之前设置的定时器
if (remaining <= 0) {
fun.apply(that, _args);
previous = Date.now();
} else {
timer = setTimeout(function() {
fun.apply(that, _args);
}, remaining); // 因为上面添加的clearTimeout.实际这个定时器只有最后一次才会执行
}
};
}
// 需要节流的事件
function sayThrottle() {
console.log("节流成功!");
}
理解区别:防抖,完全禁止频繁触发;节流将频繁触发改为规定时间内触发