防抖节流——从爱抖腿的机器人说起

444 阅读5分钟

话说印度研发了最新款的智能机器人,代号“七弟”,用于执行特殊任务。

由于开发者的大意疏忽,七弟的内核程序中存在一个隐晦的bug:当周围播放电子音乐时,电子音乐中强烈且带节奏的声波会影响七弟周围的空气密度,进而干扰里面电子元件的电容电压值,当电容释放时会执行一段固定的步行程序。但是电音中的节拍时长限制导致步行程序只能执行一半就被回退至起始状态。由于难以修复,开发者便称之为关节机械部位强化训练功能,又称“抖腿”

防抖之路

七弟的内核关键代码已经超过8千万行,其中某些模块一个函数就超过20万行,调用的话需要必须传527个参数。没有人愿意再维护这个遗留项目。好在七弟内置了深度学习,他通过自我更新不断强化自己的软硬件能力。有时在看书的时候他会播放一首电音,抖腿使他的腿部神经网络容错率更大,自我修复能力更强。甚至七弟产生了一种很奇妙的感觉——“快乐”。

似乎一切都还不错,直到遇到了服役以来最强对手——“鸟人”

鸟人通过研究七弟的源码发现了他的这个bug,于是在一次和七弟的大战中鸟人播放了一首 《Move your Body》,果不其然,七弟在这生死攸关的时刻居然开始抖腿了,关键是控记不住他记几啊。最后被鸟人拆成了零件

好在七弟还有一个给力队友——“七妹”,七妹git clone了七弟的项目代码,重新构建打包,npm run build。经过10分钟艰苦不懈的努力,七弟成功复活。

七弟痛定思痛,开始思考防抖算法。首先,这段存在bug的代码不能直接删掉,因为这段代码执行的是步行程序,去掉就TM不能走路了。那么,只能增加一些逻辑,判断一下周围是否正在播放电子音乐,如果是那就取消执行这段步行程序,如果电音停了,那就执行一次。这样的话,一首电音5分钟,只会抖一下,而不是每秒抖两下(600下)。

说干就干,七弟脱掉衣服在自己内核中加上了一段防抖代码:

let timer
/* 走路时不抖腿 */
function preventShakeLegWalk () {
  clearTimeout(timer) // 防止频繁执行步行程序,导致抖腿
  timer = setTimeout(walk, 500)
}

经过两分钟细致缜密的测试,git push 上线。

防抖成功的七弟如获新生,没有了致命缺陷。是时候和鸟人来波生死局了。

就这样在你一拳我一拳的胶着战斗中,七弟发现了鸟人的软肋——害怕高频电磁波。七弟佯装战败引鸟人来到雷达信号塔附近,在开发人员的帮助下成功终结掉了鸟人,拯救了处于水火之中的孟买市。

节流

鸟人事件之后,七弟继续优化自己的代码和功能。为了能记录机器人的实时位置。七弟在步行代码中加上了日志上传,但每秒上传一条日志流量开销巨大,要知道印度的某些地方信号较弱,并且七弟内置的流量卡是每个月1G,用完之后基本处于断网状态。

为了节省流量,并且实时监测七弟的地理位置。需要对上面的步行代码再次进行优化。

let timer
let last = 0 // 上次调用时间戳
function preventShakeLegWalk () {
  clearTimeout(timer)
  // 每分钟上传一次日志
  if (Date.now() - last > 60 * 1000) log()
  timer = setTimeout(() => {
    log()
    walk()
  }, 500)
}

这样就避免了七弟听电子音乐时会导致很长时间不上传位置,处于失联状态。

加了防抖和节流后,七弟的功能更加完善。最后和七妹过上了幸福快乐的生活。

言归正传

好了,我实在编不下去啦🤣

防抖主要针对事件连续触发的场景,且没有明确的开始和结束事件。比如touchmove有touchstart和touchend可以知道开始和结束,input和scroll事件就没有对应的开始和结束事件。而我们又往往需要在开始和结束搞点事情。比如输入框在用户停止输入时进行联想提示;页面开始滚动时隐藏页面右下角的悬浮元素,停止滚动时再显示出来;监听文件变更,一堆文件发生变化后集中编译一次。这些都是需要防抖的场景,也就是在结束时只执行一次就好。

节流是在防抖的基础上,加了固定时间调用一次,以防止回调函数很长时间才触发一次的情况。比如坦克对战游戏中点击屏幕发射一颗子弹,如果用户一直连续点,那防抖函数一直执行不了,子弹就发不出来。这时候就需要固定时间执行一次。

Talk is cheap. Show me the code!

至于代码,lodash 已经有了不错的实现,debouncethrottle,感兴趣的可以去扒扒源码。或者如果想要简单的实现,可以参考上面七弟的写法,虽然扯淡,但思路是对的😁

参考

css-tricks.com/debouncing-…