day15 防抖与节流

222 阅读3分钟

每日一句

Keep your friends close, but your enemies closer.

释义:亲近你的朋友,但更要亲近你的敌人。

image.png

背景

背景1:在前端开发中,增删改查接口是必会遇到,当在新增某个产品时,输入完标题等一系列字段时,点击保存手抖了或是网卡手见点了多次,这个时候你会发现怎么新增了好几条。

背景2:就一个input框搜索,做法是当input值发生变化时调接口去查询,只要改变了就会去查,你从接口请求中会看到请求了多次,太影响性能了。

从以上两个比较常见的背景可以看出,这不是我们想要的。为了限制在短时间内高频触发函数调用情况发生,防抖和节流应运而生了。

当然这只是其中的一种思路,你也可以换其他方式去解决,比如你可以点击保存或搜索时按钮不可点,等到接口请求回来再让按钮可点,中间的等待加上loading效果等。

防抖和节流是两种解决的策略,防抖和节流有时候会让我们迷惑,那什么是防抖,什么是节流,这两个到底有啥区别,具体怎么用等等,下面我们一起来看下。

防抖与节流

防抖debounce

顾名思议:防止抖动。一段时间内,事件在我们规定的间隔 n 秒内多次执行,回调只会执行一次。

代码实现:

  • 非立即执行版本
<button id="save">保存</button>
<script>
function debounce(callback, wait) {
  let timer;
  return () => {
    if (timer) clearTimeout(timer)
    let ctx = this;
    timer = setTimeout(() => {
      callback.call(ctx, arguments)
    }, wait)
  }
}

function saveContent() {
  console.log('save')
}
save.onclick = debounce(saveContent, 1000)
  • 立即执行版本
<button id="save">保存</button>
<script>
function debounce(callback, wait) {
  let timer;
  return () => {
    if (timer) clearTimeout(timer)
    let ctx = this;
    const callNow = !timer
    timer = setTimeout(() => {
      timer = null
    }, wait)
    if (callNow) callback.apply(ctx, [...arguments])
  }
}

function saveContent() {
  console.log('save')
}
save.onclick = debounce(saveContent, 1000)
  • 优雅的封装
function debounce(callback, wait, immediate = false) {
  let timer;
  return () => {
    if (timer) clearTimeout(timer)
    if (immediate) {
      let callNow = !timer;
      timer = setTimeout(() => {
        timer = null
      }, wait)
      if (callNow) callback.apply(this, [...arguments])
    } else {
      timer = setTimeout(() => {
        callback.call(this, arguments)
      }, wait)
    }
  }
}

节流throttle

顾名思议,控制流量。指连续触发事件但是在 n 秒中只执行一次函数。

代码实现:

  • 定时器版本
function throttle(callback, wait){
      let timer;
      return () => {
        if (!timer) {
          timer = setTimeout(() => {
            timer = null;
            callback.apply(this, arguments)
          }, wait)
        }
      }
    }
function saveContent() {
  console.log('save')
}
save.onclick = throttle(saveContent, 1000)
  • 时间戳版本
 function throttle(callback, wait){
  let pre = 0;
  return () => {
    let now = Date.now();
    if (now - pre >= wait) {
      callback.apply(this, arguments)
      pre = Date.now()
    }
  }
}

function saveContent() {
  console.log('save')
}
save.onclick = throttle(saveContent, 1000)

  • 优雅的封装
 // type 0: 定时器版 1: 时间戳版
function throttle(callback, wait, type = 0){
  if (type === 0) {
    var timer;
  } else if (type === 1) {
    var pre = 0;
  }

  return () => {
    if (type === 0) {
      if (!timer) {
        timer = setTimeout(() => {
          timer = null;
          callback.apply(this, arguments)
        }, wait)
      }
    } else if (type === 1) {
      let now = Date.now();
      if (now - pre >= wait) {
        callback.apply(this, arguments)
        pre = Date.now()
      }
    }
  }
}

function saveContent() {
  console.log('save')
}
save.onclick = throttle(saveContent, 1000, 0)

防抖和节流的区别

防抖:对于连续的事件响应我们只需要执行一次回调

节流:一个函数执行一次后,只有大于设定的执行周期后才会执行第二次

应用场景

防抖:

  • resize/onscroll事件
  • button新增ajax请求一次就好

节流:

  • DOM拖拽mousemove, 计算机鼠标移动距离mousemove
  • mousedown/keydown 事件(单位时间只能做一件事)
  • 监听滚动事件,如上拉加载,无限滚动等