防抖与节流

154 阅读2分钟

防抖

防抖原理

  • 防抖函数的作用就是控制函数在一定时间内的执行次数。防抖意味着N秒内函数只会被执行一次,如果N秒内再次被触发,则重新计算延迟时间。

防抖的应用场景

  • 搜索框输入查询,如果用户一直在输入中,没有必要不停地调用去请求服务端接口,等用户停止输入的时候,再调用,设置一个合适的时间间隔,有效减轻服务端压力。
  • 表单验证。
  • 按钮提交事件。
  • 浏览器窗口缩放,resize事件(如窗口停止改变大小之后重新计算布局)等。

防抖函数实现

 function debounce(func, wait, immediate = true) {
    let timeout, result
    // 延迟执行函数
    const later = (context, args) => setTimeout(() => {
        timeout = null   // 倒计时结束
        if(!immediate) {
            // 执行回调
            result = func.apply(context, args)
            context = args = null
        }
    }, wait)
    let debounced = function(...params) {
        if(!timeout) {
            timeout = later(this, params)
            if(immediate) {
                // 立即执行
                result = func.apply(this, params)
            }
        } else {
            clearTimeout(timeout)
            // 函数在每个等待时延的结束被调用
            timeout = later(this, params)
        }
        return result
    }
    // 提供在外部清空定时器的方法
    debounced.cancel = function() {
        clearTimeout(timeout)
        timeout = null
    }
    return debounced
 }

immediate为true时,表示函数在每个等待时延的开始被调用。immediate为false时,表示函数在每个等待时延的结束被调用。

节流

节流原理

  • 节流函数的作用是规定一个单位时间,在这个单位时间内最多只能触发一次函数执行,如果这个单位时间内多次触发函数,只能有一次生效。

节流的应用场景

  • 按钮点击事件
  • 拖拽事件
  • onScoll
  • 计算鼠标移动的距离(mousemove)

节流函数实现

function throttle(func, wait, options = {}) {
   var timeout, context, args, result
   var previous = 0
   var later = function() {
       previous = options.leading = false ? 0 : (Date.now() || new Date().getTime())
       timeout = null
       result = func.apply(context, args)
       if(!timeout) {
           context = args = null
       }
   }
   
   var throttled = function() {
       var now = Date.now() || new Date().getTime()
       if(!previous && options.leading === false) {
           previous = now
       }
       // remaining 为距离下次执行 func 的时间
       // remaining > wait, 表示客户端系统时间被调整过
       var remaining = wait - (now - previous)
       context = this
       args = arguments
       // remaining 小于等于0,表示事件触发的事件间隔大于设置的wait
       if(remaining <=0 || remaining > wait) {
           if(timeout) {
               // 清空定时器
               clearTimeout(timeout)
               timeout = null
           }
           // 重置 previous
           previous = now
           // 执行函数
           result = func.apply(context, args)
           if(!timeout) {
               context = args = null
           } else if(!timeout && options.trailing !== false){
               timeout = setTimeout(later, remaining)
           }
           return result
       }
   }
   
   throttled.cancel = function() {
       clearTimeout(timeout)
       previous = 0
       timeout = context = args = null
   }
       
   return throttled
}

禁用第一次首先执行,传递 {leading: false} ;想禁用最后一次执行,传递 {trailing: false}