长期浸泡于业务中,感觉脑回路都要变短了,所以偶尔也要抱个笔记本,点杯喝的,坐在星巴克,附庸一下风雅。既然在咖啡厅,我们就来聊点有意思的,比如一些有意思的算法函数,有空我们一起琢磨琢磨。
限流函数,相信大部分人都有接触过,一些优秀的库诸如underscore、lodash都有提供限流工具函数。这是一个有意思,且十分有效的函数,可以帮助我们在很多情况下对一些高频次操作进行有效的优化。那么,什么是限流函数呢? 举个简单的栗子,假设你喜欢上了一个小姐姐,你每天都很想念她,无时无刻不想知道她的动态,刚好碰上你又是个直男,可能你会一刻不停地给她发信息,想知道她现在在做什么。那么假设,我们发消息的频率是每隔10s一条爱的问候,也就意味着小姐姐一个小时之内,就会接受你的骚扰轰炸高达3600/10=360次,我猜,你下次要是碰到她,她会以10公里/s的速度打飞你,空接200公斤的一记重拳,然后你会以每秒720度的速度在空中旋转并以12公里/s的速度垂直下落。小姐姐们需要有自己的空间,你这么做,完全无异于对你自己的服务器发起了一次次DDos攻击,你以为的爱,就变成了无心伤害。当然了,爱情都是令人盲目的,我当然明白你很难控寄你寄己,所以作为一台呵护主人的好手机,当然要为你着想了,既然你无法自拔,那就只好暗中祝你一臂之力了,每隔1分钟才能允许发出一条爱的信息,1分钟内,尽管你每隔10s就点击发送了一条消息,但是爱你的机,其实悄悄把你的消息都自动过滤掉了,虽然你有很多消息没被你心爱的小姐姐看到,但是请你相信,她已经足够明白你的心意了。 好了,作为一个程序员,不能这么放浪形骸太久,还是要回归无聊。程序说话,接下来我们就具体来说说,我们应该如何设计这个小算法,帮助我们的大直男来限流住他喷薄的思念。请看下图
timeout=setTimeout(()=>{发送第一天消息},TA-T1)
。紧接着我们在T2又发送了一条,那这条消息是不是直接忽略就可以了,那么我们做个判断即可:
if(!timeout){
timeout = setTimeout(()=>{执行动作},TA-T1);
timeout = null;
}
复制代码
一不小心就泄漏了天机,核心就这两行了。实在对不住,让大家失望了,真的就这么多。还有个比较重要的注意点就是每次达到执行节点例如上图A,B,都需要更新执行点的时刻TA,TB,因为我们需要判断操作是否在间隔时间内。接下来,该是祭出我们的终极代码的时候了:
function throttle(fn, interval){
var last = new Date().getTime();
var timeout;
return function later(){
var context = this;
var args = arguments;
var now = new Date().getTime();
var delta = now - last;
if(delta < interval && delta >= 0){//间隔时间小于interval,需要被限流
if(!timeout){//若无定时,说明上一次执行点已过
timeout = setTimeout(()=>{
fn.apply(context,args);
last = now + interval;//记录上一次具体执行时间点
timeout = null;
},interval - delta);
}
}
}
}
复制代码
碉堡了。就这么点破玩意也敢献丑,真是让大家见笑了。好了,让我们回到前面小哥哥骚扰小姐姐事件,看看这个限流函数要如何拯救他,示例代码如下:
function sendMsg(i){
console.log("小哥哥发送第"+i+"条信息");
fn(i);
if(i < 10){
setTimeout(()=>{sendMsg(i)}, 100);
i++;
}
}
sendMsg(1);
复制代码
这里,为了演示方便,我们把限流函数的执行间隔设置300ms,使用定时函数设置发送的间隔为100ms,结果如下:
这样就达到了限流的效果,成功拯救了小哥哥的直男癌。限流的思路以及实现就分析到这里了,看过下划线或者lodash库限流函数的朋友可能会觉得我这个似乎不太一样,其实并无很大区别,只不过lodash用的是递归的方式,并且考虑了更加全面的场景,但是只从限流角度来看,个人觉得我的这个限流函数从理解上会更加容易,有兴趣的朋友可以详细对比一下,递归和非递归的实现方式,区别仅一点,那就是执行时间点的重置。
作为一名有独立思考的程序员,需要探讨的当然不止于如此,我们继续深入研究研究。不同版本实现的限流都会有一些区别,基于业务场景不同,衍生出不同的限流方式。试想,当我们每次触发的动作都是一样的,例如我们对一个按钮发起无数次的点击,每一次点击都会发起一次服务器请求,但是每次的动作都是一样的,我们要做的只是限制不能点击过快,那么,只需要按照上面的方式限流即可实现,让部分点击无效就是了。但是让我们再回过头看上面的小哥哥,虽然我们帮他限流了,但是其实中间好几条信息都直接丢失了,作为用户,这是不满足基本需求的,那这个限流方式就是需要调整的。当我们的后台服务由于用户访问量过大而不得不做限流,但也总不能将用户请求直接丢弃吧,而是将用户的请求hold住,延迟调用。
总之,限流函数的意义就不言而喻了,远不止我们看到的一个函数那么简单,不过我这里就不过多深入了,免得说错太多贻笑大方,点到即止。
感谢捧场。如有谬误,敬请指出。