因为要做一个记忆翻牌配对小游戏,然后在学习时偶然看到“节流”二字,然后就顺便去看了看所谓的“节流”

节流

1.啥是节流

n 秒内只执行一次事件,即使n 秒内事件重复触发,也只有一次生效。

如果这不好理解,那就假设一个场景:

1.你设计了一个表单,这个表单提交的数据内容很多。

2.你的有些用户闲得很无聊,写完表单以后疯狂点击提交按钮。

3.你的后端同事走到你面前指着崩溃的服务器来向你抱怨。

这就像打游戏放技能一样,放完一次技能后就有一段的冷却时间

所以我的第一想法肯定是给这个button一个冷却时间

现在在我面前的有两个东西:一个按钮提交的功能,一个冷却时间,假设为5秒

牵扯到时间会让我第一时间想到 setTimeout和setInterval,首先排除setInterval,因为提交表单这个功能我不希望它循环执行,所以我选择setTimeout。

假设你现在正在玩游戏,游戏有一个技能,它的技能CD是两秒,那么我们就需要判断用户是否在两秒内多处点击了这个技能,如果多次点击,那么无事发生(返回一个空函数),如果不在CD,那么返回这个技能的特效(技能执行的函数)

const fnOnclick = ()=> console.log("技能已经开启!")
<button onClick ={cdTime(fnOnclick,2000)}>技能</button>

在这里我们需要知道cdTime这个函数只是一个外壳函数,它真正的意义在于传递我们需要的参数,它并不是我们希望onclick真正要执行的函数!cdTime返回的那个函数才是我们真正想执行的函数

现在我们设计cdTime,也就是我们限制 技能 只能在两秒之内释放。

2.代码实现
const fn0nClick = () => console. log("技能已经开启!!") 
//这就是你按钮提交的onclick事件
function cdTime(fn, delay) {
let CD =false; //首先你的技能刚开始是没有冷却时间的
return function ()
if (CD) { //ok, 当你想放技能的时候,你需要判断是否在冷却时间内,如果在,对不起不能放!
console. log ("不行,cd中" )
return false; //这里写null, 空值都可以
}
CD = true; //这步我的技能还没放,即将要释放。因为setTimout是异步执行, 所以cd在两秒以后才会被修改,这一步是限制用户频繁点击技能键时,让函数返回一个空值。
setTimeout(() => {
//现在没有进入cd,当你放技能的时候然后开始启用
console. log("技能成功释放");
CD = false; //ok, 现在我技能释放完毕,把cd的属性清空。
fn();

}, delay);
}
}

然后我在看完节流后又摸过去看了看防抖

防抖

1.啥是防抖

在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。

我们拿一个点击按钮来举例子。

function getSearch() {
console.log("发请求")
}
<button onClick =()=>{getSearch}>点击</button>

现在我们尝试疯狂点击按钮就会疯狂发送请求。

img

我们现在来修改一下这个函数,我们思考一下🤔,假设我们不借助 debounce 可以实现一个伪防抖的功能吗?答案是百分百可以的。我们先在这个文件下设定一个变量叫做 timerID

let timerId = 0

然后我们设定一个定时器,来使这个 console.log(“发请求”) 在 1.5s 后执行。

let timerId = 0
function getSearch(){
timerId = setTimeout(()=>{
console.log("发请求")
},1500)
}

然后这里要补充讲一下:setTimeout的返回值

imgimg

其实 setTimeout 会在 setTimeout 执行的时候返回一个大于 0 的正整数。 所以我们这句话其实是在给 timerID 赋值! 并不是将 setTimeout 函数本身赋值给 timerID 这个变量。

img

这里我们需要特别搞清楚 setTimout函数本身执行的时候,是马上赋值的,并不是等到 1.5s 后再赋值的。什么意思呢? 我设置了大约在10几年后再执行的一个函数,千万不要觉得 timerID 是会在10年以后才会被赋值。

为什么要这样设计呢?因为如果这样执行的话,就会给我们一个反悔的机会。还说上面的例子。假设我在 5 年后突然反悔不想执行了。我只需要取消这个 timerID 就可以中途放弃执行。

function cancelSearch(){
console.log("我取消的ID是",timerId)
clearTimeout(timerId)
}

这个 timerID 就是每一个 setTimeout 的身份证。每当你执行一次 setTimout 后,setTimout 所接收的回调函数就会被分配一个唯一 ID,来被放进任务队列。 而 clearTimeout 的功能恰好就是清除位于任务队列里指定的 id 所绑定的那个回调函数。

2.代码实现
let timerId = 0

function getSearch(){

if(timerId){
clreaTimeout(timerId)
}
//当我们每次执行 getSearch 之前,如果当前任务队列里有上一次同样的任务,我们就先清除掉

timerId = setTimeout(()=>{
console.log("发请求")
},1200)
//然后再去开启一个定时器任务推进任务队列
}