函数节流与函数防抖

函数节流与函数防抖

函数节流和函数防抖是一个老生常谈的话题了,两者都是对大量频繁重复调用代码的一种优化方案 今天在某群和大家讨论时,顺便搜了一些相关博客 发现有一篇关于两者的定义竟然写反了。。。所以决定自己来写一下-.-权当加深记忆了

函数节流(throttle)

正如其命名的含义,节流。 限制函数在一定时间内调用的次数。

类似的实际生活中的场景

早晚高峰的地铁排队。

太多的人拥挤到站台上,大家都想搭上这班车,人挤人之间,难免会出现一些问题。 所以在很多地铁站,高峰期会设置很多层的屏障,来增加你进站的时间,从而减少站台的压力。

在程序中的实践

同理,代入程序中,我们可以通过限制函数调用的频率,来抑制资源的消耗。 比如我们要实现一个元素拖拽的效果,我们是可以在每次move事件中都进行重绘DOM,但是这样做,程序的开销是非常大的。 所以在这里我们可以用到函数节流的方法,来减少重绘的次数:

// 普通方案
$dragable.addEventListener('mousemove', () => {
  console.log('trigger')
})

// 函数节流的实现方案
let throttleIdentify = 0
$dragable.addEventListener('mousemove', () => {
  if (throttleIdentify) return
  throttleIdentify = setTimeout(() => throttleIdentify = 0, 500)
  console.log('trigger')
})

这样来做的话,在拖动的过程中,能保证500ms内,只会重绘一次DOM。 在同时监听了mouseover后,两者最终的效果是一致的,但是在拖动的过程中,函数节流版触发事件的次数会减少很多,遂消耗更少的资源。

通用的函数节流实现

/**
 * 函数节流的实现
 * @param  {Function} func      要实现函数节流的原函数
 * @param  {Number}   interval  节流的间隔
 * @return {Function}           添加节流功能的函数
 */
function throttle (func, interval) {
  let identify = 0
  return (...args) => {
    if (identify) return
    identify = setTimeout(() => identify = 0, interval)
    func.apply(this, args)
  }
}

类似函数节流的操作

平时开发中经常会做的ajax请求获取数据,这里可以用到类似函数节流的操作。 在我们发送一个请求到后台时,当返回的数据还没有接收到时,我们会添加一个标识,来表明当前有一个请求正在被处理,如果这时用户再触发ajax请求,则会直接跳过本次函数的执行。 同样的还有滑动加载更多数据,如果不添加类似的限制,可能会导致发送多条请求,渲染重复数据。


我曾经在某软件里遇到过-.-连续点击登录按钮数十次,结果连着给我弹了三次密码错误,随后告诉我输入密码错误超过三次,您的账号已被锁定。

函数节流的定义:限制函数在一定时间内调用的次数

函数防抖(debounce)

是函数在特定的时间内不被再调用后执行。

实际的例子

还是拿城市交通工具来说事儿。。 坐公交时,到站了,是由司机来操作车门的开合的。 当司机准备离站时,关闭车门,这是突然跑过来一人,司机只好再将车门打开,让人上来。

又或者,乘坐升降电梯时,电梯门关闭后,外边跑来一人又将电梯门按开。 这两件事儿都是因为关门这一个事件处理太快导致的,徒增一次开关门的消耗。

在程序中的实践

监听窗口大小重绘的操作。

在用户拖拽窗口时,一直在改变窗口的大小,如果我们在resize事件中进行一些操作,消耗将是巨大的。 而且大多数可能是无意义的执行,因为用户还处于拖拽的过程中。 所以我们可以用函数防抖来优化相关的处理

// 普通方案
window.addEventListener('resize', () => {
  console.log('trigger')
})

// 函数防抖方案
let debounceIdentify = 0
window.addEventListener('resize', () => {
  debounceIdentify && clearTimeout(debounceIdentify)
  debounceIdentify = setTimeout(() => {
    console.log('trigger')
  }, 300)
})

我们在resize事件中添加了一个300ms的延迟执行逻辑。 并且在每次事件触发时,都会重新计时,这样做也就可以保证,函数的执行肯定是在距离上次resize事件被触发的300ms后。 两次resize事件间隔小于300ms的都会被忽略,这样就节省了很多无意义的事件触发。

输入框的输入联想

几乎所有的搜索引擎都会对你输入的文字进行预判,并在下方推荐相关的结果。 但是这个联想意味着我们需要将当前用户所输入的文本传递到后端,并获取返回数据,展示在页面中。 如果遇到打字速度快的人,比如260字母/分钟的我,在一小段时间内,会连续发送大量的ajax请求到后端。 并且当前边的数据返回过来后,其实已经失去了展示的意义,因为用户可能从you输入到了young,这两个单词相关的结果肯定是不一样的。 所以我们就可以在监听用户输入的事件那里做函数防抖的处理,在XXX秒后发送联想搜索的ajax请求。

通用的函数防抖实现

/**
 * 函数防抖的实现
 * @param  {Function} func   要实现函数节流的原函数
 * @param  {Number}   delay  结束的延迟时间
 * @return {Function}        添加节流功能的函数
 */
function debounce (func, delay) {
  let debounceIdentify = 0
  return (...args) => {
    debounceIdentify && clearTimeout(debounceIdentify)
    debounceIdentify = setTimeout(() => {
      debounceIdentify = 0
      func.apply(this, args)
    }, delay)
  }
}

类似函数防抖的操作

在一些与用户的交互上,比如提交表单后,一般都会显示一个loading框来提示用户,他提交的表单正在处理中。 但是发送表单请求后就显示loading是一件很不友好的事情,因为请求可能在几十毫秒内就会得到响应。 这样在用户看来就是页面中闪过一团黑色,所以可以在提交表单后添加一个延迟函数,在XXX秒后再显示loading框。 这样在快速响应的场景下,用户是不会看到一闪而过的loading框,当然,一定要记得在接收到数据后去clearTimeout

let identify = setTimeout(showLoadingModal, 500)
fetch('XXX').then(res => {
  // doing something

  // clear timer
  clearTimeout(identify)
})

函数防抖的定义:函数在特定的时间内不被再调用后执行

总结

函数节流、函数防抖 两者都是用来解决代码短时间内大量重复调用的方案。 当然,也是各有利弊。 在实际开发中,两者的界定也很模糊。 比如搜索关键字联想,用节流或者防抖都可以来做,拖拽DOM、监听resize等等,这两个都是可以来实现的。

两者的区别在于: 函数节流在一定时间内肯定会触发多次,但是最后不一定会触发 函数防抖可能仅在最后触发一次

记住上边这两句,我觉得遇到类似需要进行优化的场景,应该就能够知道该用哪个了。

参考资料

  1. Javascript debounce vs throttle function
  2. Javascript function debounce and throttle

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏jeremy的技术点滴

免编译在JSP中直接写react代码

64350
来自专栏前端侠2.0

bootstrap-datetimepicker的功能优化

引用:bootstrap 和 bootstrap-datetimepicker CSS文件

45510
来自专栏魏艾斯博客www.vpsss.net

代码实现 WordPress 文章中英文数字间自动添加空格

26930
来自专栏一个会写诗的程序员的博客

一篇文章读懂 React and redux 前端开发 -DvaJS, a lightweight and elm-style framework.快速上手Dva 概念 #例子和脚手架Dva 图解K

DvaJS: React and redux based, lightweight and elm-style framework.

22130
来自专栏云瓣

通过一个demo了解Redux

TodoList小demo 效果展示 项目地址 (单向)数据流 数据流是我们的行为与响应的抽象;使用数据流能帮我们明确了行为对应的响应,这和react的状态可预...

346100
来自专栏小狼的世界

学习使用YUI3

对YUI一直很有好感,最近开始看了看YUI CSS GRID,发现这个理念非常好,非常有利于我们工作效率的提高,特别是熟悉了这套CSS之后,我们就不必每一个项目...

9020
来自专栏何俊林

SuperIndicator开源库源码分析

背景:为什么有这个库,在经常开发中,会碰到,显示广告位图片,精彩推荐,及比较好的,希望能直观显示到用户看的。那么SuperIndicator正是这样一个类库。 ...

20550
来自专栏IT派

机器学习新手必看:Jupyter Notebook入门指南

【导读】Jupyter Notebook 是一个 Web 应用程序,便于创建和共享文学化程序文档,支持实时代码、数学方程、可视化和 Markdown,其用途包括...

45640
来自专栏云计算教程系列

如何在Python 3中安装pygame并创建用于开发游戏的模板

Pygame库是专门为了帮助您做出的游戏和其他多媒体应用Python编程语言的一个开放源代码模块。pygame 构建于高度可移植的SDL(Simple Dire...

80520
来自专栏封碎

Android的webview研究

最近做的项目大量用到了 webview ,用网页来布局。 Android 的 webview 是基于 webkit 内核,不过他的运行效果和 firefox 上...

13610

扫码关注云+社区

领取腾讯云代金券