简单入门到彻底搞懂防抖和节流

       今日头条出的题目是写一个防抖函数,,根据用户输入的词语,提交后端进行模糊查询,按输入次序显示服务器返回结果。
    
       原题目,面试官写的是注意防抖和次序。

      他的要求是: 比如输入非常快的提交了三次 a, b,c 三个字母。由于服务器响应慢,客户端已经提交c查询,但是服务器才返回的b的查询结果(这个用闭包,其实来闭包也有问题,典型如何处理异常)。
      

        面试官出的这道题的确很有代表性,前端开发中经常会遇到的问题,重复执行,表单重复提交,不管前后端开发都应该了解。这个题跟我大约2012年6月,腾讯网www.qq.com 首页改版上线,做的一个搜索模块很相似, 那时候在腾讯是我职业生涯中第一个前端开发工作。

        当时这个项目,填了几个坑:

         1.必须用原生javascript代码,腾讯网首页那时候可不让你引入jquery,引入一个大js这么大的访问量得浪费多少钱?:)
         2.就是层遮盖的问题,输入关键字 关键字弹层是摊在输入框下方,那时候搜索框下面有flash,一不小心就被flash盖住
   最坑的是还有飞来飞去的广告
         3.其次就是后端返回的智能提示元素个数不相同,还有可能不返回,元素个数不同,就需要代理机制,冒泡到上层处理
         4.另外就是css的问题,网页/图片/视频等栏目蓝色框是要覆盖的,各位看官仔细看,鼠标移动到“网页”下拉前下面是蓝色边框,移动过去后变没了。最先版本还记录用户鼠标从哪移的,点击了那个栏目,再跟关键词点击关联就跟复杂了。

         5. 因为这个作为一个模块,就是css动态加载,动态加载简单,加载进来能用,不跟其他人冲突,处理起来也琐碎。

         6.与分类栏目相关的关键词搜索,以及日志记录

         7.异常处理

          另外就是面试官说的那两个问题。我觉得最经典的就是防抖。虽然简单,但却经典,稍后介绍。

 

   我做项目的那会儿,不知道是否是学艺不精,不知道有防抖的概念,那 下面就来介绍一下防抖。下面的代码测试一下,就会浪费很多资源执行程序,这里仅是输出了console.log(),对资源消耗不大,如果数据库批量查询影响就很大了。运行下面的程序,打开浏览器,滚动滚动条或者放大缩小窗口,浏览器控制台就会输出一堆log。

<html>
<head>
<meta charset="utf-8" />
<style>
  body {font-size:12px; height:2000px;}
</style>
</head>
<body>
访问页面,打开浏览器控制台,用鼠标滚动或者调整浏览器大小观看效果
<script>
//滚动
window.onscroll  = function(){
	console.log('滚动条位置:' + document.body.scrollTop || document.documentElement.scrollTop)
}
//调整窗口大小
window.onresize = () => {
      console.log('窗口宽x高:' + window.innerWidth+'x'+window.innerHeight)
}
</script>
</body>
</html>

函数防抖(debounce)

防抖函数:一个频繁触发的操作,在规定时间内,只让最后一次生效,前面的不生效。
  看防抖函数debounce(fn,delay),delay参数是1秒,1秒调用防抖函数不管多少次,只执行一次。

         防抖从字面的意思很好理解。典型的应用就是拍照,估计大家都有印象,不防抖的相机拍出来的照片有一堆重影。防抖就是让相机只拍一次。

上面项目需要防抖就是鼠标在 分类区域 移动的时候,如果用户从网页--》图片--》视频一下移动到音乐,只需要记录用户移动到了音乐即可。还有就是输入关键字非常快比如输入 菲律宾,实际查询菲律宾关键字就可以了。而不是输入“菲”查询一次“菲律”查询一次 “菲律宾”查询一次。

来一个最简单的例子,浏览器执行以下代码会发现console控制台输出很多次,但是计算资源是有限的,所以我们需要控制速度,这个好比游戏打怪,不等满血进度条完成,游戏无法开始。

上代码,看看防抖函数

/* 函数防抖,
@param fun  要防抖的函数名
@param delay 防抖间隔时间
*/
function debounce(fun, delay) {
    return  (args) => {
        clearTimeout(fun.id) //清除定时器
        fun.id = setTimeout(()=> {
            fun.call(this, args)
        }, delay)
    }
}

经过上面的例子想必你也清楚了,什么是防抖,以及在前端如何防抖。对于后端那怎么防抖呢? 有兴趣的可以我在腾讯搜搜做积分系统的时候,写过的一篇文章,用memcache或者redis实际上实现的就是防抖

http://blog.sina.com.cn/s/blog_467eb8ca0100zmvb.html

那节流又是什么鬼东西呢? 

函数节流(throttle)

函数节流
一个函数执行一次后,只有大于设定的执行周期后才会执行第二次。
节流函数,throttle(fun,delay)参数是1秒,则是不管持续多长时间,每秒执行一次,且每秒只执行一次。

这个打个比方,就是一盆水,慢慢流出来,而不是哐 一下直接倒了。这个很简单可以看示例中函数节流的那部分代码。

在上面那个 //函数节流

//函数节流
function throttle(fun,delay){
    let valid = true
    return function(args) {
       if(!valid){
           //休息时间 暂不接客
           return false
       }
       // 工作时间,执行函数并且在间隔期内把状态位设为无效
        valid = false
        setTimeout(() => {
            fun.call(this, args)
            valid = true;
        }, delay)
    }
}

在服务器端实现函数节流就很简单了,最好是放在异步执行,用sleep函数定时执行即可。

关于防抖和节流的函数 ,都写在下面了,大家在输入框快速输入字母,然后打开浏览器控制台,自行测试。

<html>
<head>
<meta charset="utf-8" />
<style>
  body {font-size:12px;}
  span { width:60px; height:22px; display:inline-block;}
</style>
</head>
<body>
	<span class="tip">无防抖测试</span> <input id="text" />
	<br>
	<span class="tip">防抖测试</span> <input id="input" />
	<br>
	<span class="tip">节流测试</span> <input id="throttle" /> <br />

</div>
<script>
//没有防抖请求
function ajax(content) {
  console.log('ajax request data ' + content)
}

let input = document.getElementById('text')

input.addEventListener('keyup', function (e) {
    ajax(e.target.value)
})

/*
*/

/* 函数防抖,
@param fun  要防抖的函数名
@param delay 防抖间隔时间
*/
function debounce(fun, delay) {
    return  (args) => {
        clearTimeout(fun.id) //清除定时器
        fun.id = setTimeout(()=> {
            fun.call(this, args)
        }, delay)
    }
}

let inputb = document.getElementById('input')
let debounceAjax = debounce(ajax, 1000)  //函数ajax加入防抖,间隔1秒
inputb.addEventListener('keyup', function (e) {
	debounceAjax(e.target.value)
})


//函数节流
function throttle(fun,delay){
    let valid = true
    return function(args) {
       if(!valid){
           //休息时间 暂不接客
           return false
       }
       // 工作时间,执行函数并且在间隔期内把状态位设为无效
        valid = false
        setTimeout(() => {
            fun.call(this, args)
            valid = true;
        }, delay)
    }
}
let throttleAjax = throttle(ajax, 1000)

let inputc = document.getElementById('throttle')
inputc.addEventListener('keyup', function(e) {
	throttleAjax(e.target.value)
})
</script>
</body>
</html>

总结

  • 函数防抖和函数节流都是防止某一时间频繁触发。
  • 函数防抖是某一段时间内只执行最后一次,而函数节流是小间隔时间执行一次。

结合应用场景

  • debounce 防抖 
    • window触发resize一次
    • 滚动操作记录最终状态
    • 鼠标不断点击触发,mousedown(单位时间内只触发一次)
    • 输入参数校验,只需要校验一次就好
  • throttle 节流
    • search搜索联想,用户在不断输入值时,节约请求资源
    • 监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断
    • 监听window 的resize多次
    • 计算鼠标移动距离
    • canvas画图
    • 射击游戏,单位时间点击按钮,单击鼠标发射一次子弹

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章