超级简单的防抖与节流

javascript/jquery

浏览数:269

2020-5-26

前言

以前准备面试、看面经的时候,总会在心里骂,怎么这么多沙雕题目,这不是特意为难我胖虎吗

参加工作一年多了,发现很多面试题在工作中真的会明的暗的碰到。
能让我等菜鸟提前知道这么多概念,为我们指明debbug的方向,真是煞费了前人的一番苦心呀。

这两天遇到一个bug正好是利用节流的思想解决的,趁此机会正好把我对防抖节流思想的理解和应用整理一下,写出了,希望能在工作和面试中帮到大家

防抖(debounce)和节流(throttle)很容易搞混,定义极其晦涩。因此我们先从应用场景入手

防抖(debounce)

应用场景

在输入文字的时候我们进行某些处理,如果直接绑定处理函数,则每次输入都会处理一次,造成性能浪费,我们实际希望的是用户输入完了或者暂时输入完了我们再去处理,所以需要给定一个延迟时间,然后如果一直输入我们一概不断去刷新这个延迟时间,直到停止输入了再延迟处理

实现

debounce函数挺简单的,就是一个延迟执行,和刷新延迟功能,用Js的定时器和清除定时器很容易搞定,写个超级简单的版本

未防抖:<input type="text" oninput="handleInput()">
防抖处理后:<input type="text" oninput="debounce(handleInput,300)()"> 
function debounce (fn,delay){
    return function () {
        fn.tid && clearTimeout(fn.tid)
        fn.tid=setTimeout(fn,delay)
    }
}

节流(throttle)

应用场景

登录按钮,用户登录的时候点一下,假设报错,账号密码错误
如果用户手贱,一直狂点,会一直出提示,需要优化一下,先看下效果
未使用节流:重复动作忒多

未使用节流:动作一定时间内只执行一次

实现

怎么实现throttle(fn,delay)这个函数呢,单位时间内只能执行一次,换句话说当前执行的时候与前一次执行时间差必须大于delay才让执行
我们要保存上一次执行时间,咋保存呀?通过闭包
写个超级简单的,网上看了其他几个人的,好像和我不太一样if后面还跟了个else,使单位时间后还会执行一次,这个就看个人需求啦哈哈哈,主体逻辑绝壁是我这个,大家放心大胆抄,原创

throttle(fn,interval){
            let lastTime = new Date().getTime()//闭包保存上一次时间,注意这个函数执行的时候LastTime就存在了,所以一般点击的时候再触发,fn基本会执行,也就是先执行一次,然后单位时间执行一次
            return function () {
                let currentTime = new Date().getTime()
                if(currentTime-lastTime>interval){
                    fn()
                    lastTime = currentTime
                }
            }
        }

完整代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
    <!-- 引入组件库 -->
    <script src="https://unpkg.com/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>
</head>
<body>

<div id="app">
    未节流:<el-button @click="handleLogin">login</el-button><br/><br/><br/>
    节流:<el-button @click="throttleHandleLogin">login</el-button>
</div>

<script >
new Vue({
    el: '#app',
    data: function(){
        return {
            throttleHandleLogin: this.throttle(this.handleLogin,2000)
        }
    },
    methods: {
        handleLogin(){
            this.$message({message:'账号密码错误',type:'error'})
        },
        throttle(fn,interval){
            let lastTime = new Date().getTime()
            return function () {
                let currentTime = new Date().getTime()
                if(currentTime-lastTime>interval){
                    fn()
                    lastTime = currentTime
                }
            }
        }
    }
})


</script>

</body>
</html>

问大家一个问题,为什么我会把throttleHandleLogin写在data里面?写在methods里面行不行?想知道答案的点赞留言

防抖节流区别

看起来防抖节流都是解决动作重复触发产生的性能问题的,但是他们的原理是不同的
debounce 是延迟执行,刷新延迟(理论上如果我一直动是不是可以一直不执行?)
throttle 是立刻执行,间隔执行,(单位时间肯定得执行,应该不会有人会无聊到把间隔时间设置为无限长吧)
所以区别可以记成,当动作一直持续下去,函数会不会执行。debounce不会,throttle会

还是记不住区别的记使用场景吧

解决一个开发问题

现在运用我们所学解决上个星期我遇到的一个场景
我在axios里面有个响应拦截器,遇到后端返回的状态码为xx(没有权限)会跳到登录页,报错没有权限
但是有个问题,在某些页面我会不止发一个请求,每个请求都会受到没有权限的响应引起报错,所以跳到登录页我会受到N个没有权限的提示

怎么做?用debounce还是throttle?

我用的throttle解决的,3秒内如果是权限问题,只让报一次错

//fn是权限错误的相关逻辑
const throttlefn = throttle(fn,3000,{trailing:false})
service.interceptors.response.use(
  async res => {
    if (!res.data) return null
    const { data: { msg, status } } = res
    switch (status) {
      case OK:...
      case NOT_LOGIN:case LoginFailed:case InvalidRole:case INVALID_PWD:
        throttlefn(msg)
        break
      case NONE_DATA:...
      default:...
    }
  },...)

结尾

今天关于节流防抖的优化就分享到这了。由于技术有限,如果阅读中发现有什么错误,请在留言指出
如果你觉得本文对你有很大的帮助,求点赞,求收藏,求打赏,你们的支持是作者写作的最大动力!

作者:Runningfyy