防抖函数

127 阅读2分钟

一.debounce函数原理

1.简单写如下

function debounce() {
    let t = null
    if(t !== null){
        clearInterval(t)
    }
    t = setTimeout(()=>{
        console.log('zzz')
    },500)
}

这样写业务逻辑和防抖函数混合在了一起,不是很好

2.改进如下

 debounce(function (){
     console.log('zzz')
 },delay)
function debounce(fn,delay){
    let t = null
    return function(){
      if(t !== null){
        clearInterval(t)
       }
       t = setTimeout(()=>{
        fn.call(this)
       },delay)
    }
}
​

那我们封装防抖函数的思路是什么呢?就是利用异步来进行,将本该进行的操作延后,以下面程序为例

程序开始运行的时候,将业务逻辑代码放入定时器,定时器放入异步调用队列,所以第一次执行完以后,异步调用队列里面有一次定时器操作,此时如果再次进行操作的话,那么第二次t不为空,刚好被clearTimeout清除,所以怎么被延迟的呢,就是我上一步加入了异步队列,然后下一步执行的时候,你把我给干掉了,然后将第二次的定时器加入了异步队列。直到你不再调用防抖函数

3.下面我们进行更加完善的封装

function debounce(fn,delay){
    let t = null
    const _debounce = function(...args){
        if(t){
            clearTimeout(t)
        }
        t = setTimeout(()=>{
            fn.apply(this,args)
        })
    }
    return _debounce
}

3.1.立即执行功能

刚开始输入的时候,就发送一次请求,然后开始延迟调用利用一个第三方变量来控制,最好不要随意改变输入的参数的内容,刚开始的时候定义isInvoke 这个变量用来动态控制第一次输入立即执行,immediate控制是否立即执行,false就是不允许,true就是允许,把它当做参数传递进去,而不是随意改变它的值,我们来走一遍程序:当immediate为true 的时候,立即执行一次函数,然后将isInvoke改变为true,这样一来就下一次就不能立即执行了,除非改变为false,那么此时此刻程序就又是普通的防抖函数,

function debounce(fn,delay,immediate){
    let t = null
    let isInoke = false
    const _debounce = function(...args){
        if(t){
            clearTimeout(t)
        }
        if(immediate && !isInvoke){
           fn.apply(this,args)
            isInvoke = true
        }else{
            t = setTimeout(()=>{
            fn.apply(this,args)
                isInoke = false
           })
        }
        
    }
}

3.2.取消功能

点击按钮取消可以取消发送请求,在定时器期间用户想要取消这个输入内容 ,当我们不调用防抖函数的时候,此时,就开始调用异步队列里面的定时器,那么在定时器结束之前的期间我们可以进行将定时器删除,这样就不会执行业务逻辑了,也就不会发送请求了

function debounce(fn,delay,immediate){
    let t = null
    let isInoke = false
    const _debounce = function(...args){
        if(t){
            clearTimeout(t)
        }
        if(immediate && !isInvoke){
           fn.apply(this,args)
            isInvoke = true
        }else{
            t = setTimeout(()=>{
            fn.apply(this,args)
                isInoke = false
           })
        }
        
    }
    _debounce.cancel = function(){
        if(t){
            clearTimeout(t)
        }
        t = null
        isInvoke = false
    }
    return _debounce
}

3.3.防抖函数的返回值 可以利用promise返回

function debounce(fn,delay,immediate){
    let t = null
    let isInoke = false
    const _debounce = function(...args){
        return new Promise((resolve,reject)=>{
          if(t){
            clearTimeout(t)
          }
         if(immediate && !isInvoke){
           fn.apply(this,args)
            isInvoke = true
         }else{
            t = setTimeout(()=>{
            fn.apply(this,args)
                isInoke = false
           })
         }
     })
        
        
    }
    _debounce.cancel = function(){
        if(t){
            clearTimeout(t)
        }
        t = null
        isInvoke = false
    }
    return _debounce
}
 <script>
        var inp = document.querySelector('input')
            // 
        let count = 0
        let fn = function() {
                console.log(`发送了${++count}次请求`, this, event);
                return 'zhaobo'
            }
            // const debounceChange = debounce(fn, 500, true, (res) => {
            //     console.log(res);
            // })
            // inp.oninput = debounceChange
        const debounceChange = debounce(fn, 500, true, (res) => {
            console.log(res);
        })
        const tempClick = () => {
            debounceChange().then(res => {
                console.log('promise返回值', res);
            })
        }
        inp.oninput = tempClick
        let cancelBtn = document.querySelector('#cancel')
​
        cancelBtn.onclick = function() {
                debounceChange.cancel()
            }
            // let cancelBtn = document.querySelector('#cancel')
            // cancelBtn.onclick = function() {
            //     debounce.cancel()
            // }
    </script>

\