有趣的函数系列之(一)- 限流函数

长期浸泡于业务中,感觉脑回路都要变短了,所以偶尔也要抱个笔记本,点杯喝的,坐在星巴克,附庸一下风雅。既然在咖啡厅,我们就来聊点有意思的,比如一些有意思的算法函数,有空我们一起琢磨琢磨。
限流函数,相信大部分人都有接触过,一些优秀的库诸如underscore、lodash都有提供限流工具函数。这是一个有意思,且十分有效的函数,可以帮助我们在很多情况下对一些高频次操作进行有效的优化。那么,什么是限流函数呢? 举个简单的栗子,假设你喜欢上了一个小姐姐,你每天都很想念她,无时无刻不想知道她的动态,刚好碰上你又是个直男,可能你会一刻不停地给她发信息,想知道她现在在做什么。那么假设,我们发消息的频率是每隔10s一条爱的问候,也就意味着小姐姐一个小时之内,就会接受你的骚扰轰炸高达3600/10=360次,我猜,你下次要是碰到她,她会以10公里/s的速度打飞你,空接200公斤的一记重拳,然后你会以每秒720度的速度在空中旋转并以12公里/s的速度垂直下落。小姐姐们需要有自己的空间,你这么做,完全无异于对你自己的服务器发起了一次次DDos攻击,你以为的爱,就变成了无心伤害。当然了,爱情都是令人盲目的,我当然明白你很难控寄你寄己,所以作为一台呵护主人的好手机,当然要为你着想了,既然你无法自拔,那就只好暗中祝你一臂之力了,每隔1分钟才能允许发出一条爱的信息,1分钟内,尽管你每隔10s就点击发送了一条消息,但是爱你的机,其实悄悄把你的消息都自动过滤掉了,虽然你有很多消息没被你心爱的小姐姐看到,但是请你相信,她已经足够明白你的心意了。 好了,作为一个程序员,不能这么放浪形骸太久,还是要回归无聊。程序说话,接下来我们就具体来说说,我们应该如何设计这个小算法,帮助我们的大直男来限流住他喷薄的思念。请看下图

以上是我画的一个分析简图,我们假设这是一条时间线,A,B之间的间隔就是我们每次发送的消息的时间间隔,我们假设这个间隔时间是T,这条时间线就是由无数个间隔T组成,1、2、3三个点就是小哥哥每次发送消息,从我们限流的原理可知,A点前的1、2两条信息,由于间隔时间在<T,因此实际只会执行一次操作。我们来仔细分析一下这个时间线,假设当前A点之前的执行节点时刻为T,一条消息刚发完,紧接着,我们在T1(代表时刻1,下同)又发了一条消息1,这个时候应该怎么做呢,这条消息我们得hold住等到时间节点A的时候再执行,因此很简单,只要我们定时到A时间点的时候执行就好了 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住,延迟调用。
总之,限流函数的意义就不言而喻了,远不止我们看到的一个函数那么简单,不过我这里就不过多深入了,免得说错太多贻笑大方,点到即止。
感谢捧场。如有谬误,敬请指出。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值