Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

防抖和节流 #21

Open
kangkai124 opened this issue May 13, 2019 · 2 comments
Open

防抖和节流 #21

kangkai124 opened this issue May 13, 2019 · 2 comments
Labels

Comments

@kangkai124
Copy link
Owner

kangkai124 commented May 13, 2019

参考:
https://juejin.im/post/5cce5380f265da03826129bf
mqyqingfeng/Blog#22
mqyqingfeng/Blog#26

@kangkai124 kangkai124 added the JS label May 13, 2019
@kangkai124
Copy link
Owner Author

kangkai124 commented May 23, 2019

防抖节流都是为了降低某个事件的触发频率,减少无用的触发次数。

debounce 防抖

触发一个事件,等待n秒后执行,如果n秒内又触发了这个时间,以新的事件为准,n秒后执行,直到触发事件n秒内不再触发,才执行。

举个栗子:一部直梯,陆续往上上人(连续触发),当不再上人的时候(停止连续触发),电梯才会关门并动起来(执行方法)。

再举个栗子:

image

代码实现:

const debounce = (fn, ms = 0) => {
  let timeout
  return function (...args) {
    clearTimeout(timeout)
    timeout = setTimeout(() => fn.apply(this, args), ms)
  }
}

立即执行

如果希望触发后可以立即执行,等停止触发n秒后,才可以重新触发执行。

const debounce = (fn, ms, immediate) => {
  let timeout
  return function (...args) {
    if (timeout) clearTimeout(timeout)
    // timeout为空表示第一次触发
    if (immediate && !timeout) {
     fn.apply(this, args) 
  }
  timeout = setTimeout(() => {
    fn.apply(this, args)
  }, ms)
}

取消

增加一个取消功能,取消防抖后可以跳过等待间隔立即执行。

const debounce = (fn, ms, immediate) => {
  let timeout
  const debounced = (...args) => {
    clearTimeout(timeout)
     if (immediate) {
      let callNow = !timeout
      timeout = setTimeout(() => {
        timeout = null
      }, ms)
      if (callNow) fn.apply(this, args)
    }else {
      timeout = setTimeout(() => fn.apply(this, args), ms)
    }
  }
  
  debounced.cancel = () => {
    clearTimeout(timeout)
    timeout = null
  }
  
  return debounced
}

DEMO:See the Pen debounce-demo by kk (@kk124) on CodePen.

@kangkai124
Copy link
Owner Author

kangkai124 commented May 23, 2019

Throttle ['θrɑtl] 节流

持续触发事件,每隔一段事件只执行一次。

举个栗子:当鼠标移入的时候,事件立刻执行,每过 1s 会执行一次,如果在 4.2s 停止触发,以后不会再执行事件。

节流的实现,有两种主流的实现方式,一种是使用时间戳,一种是设置定时器。

gif

使用时间戳

代码实现:

const throttle = (fn, wait) => {
  let previous = 0
  return function (...args) {
    let now = Date.now()
    if (now - previous > wait) {
      fn.apply(this, args)
      previous = now
    }
  }
}

使用定时器

代码实现:

const throttle1 = (fn, ms) => {
  let timeout
  return function (...args) {
    if (!timeout) {
      timeout = setTimeout(() => {
        timeout = null
        fn.apply(this, args)
      }, ms)
    }
  }
}

当然,fn.apply(this, args)也可以放在 setTimeout 外部,这样的话会立即执行。

const throttle2 = (fn, ms) => {
  let timeout
  return function (...args) {
    if (!timeout) {
      timeout = setTimeout(() => {
        timeout = null
      }, ms)
      fn.apply(this, args)
    }
  }
}

image

上图显示了两者的区别,那么接下来实现一个开始立即执行,且停止触发间隔时间后也执行的节流。

const throttle = (fn, wait) => {
  let inThrottle, lastFn, lastTime
  return function (...args) {
    if (!inThrottle) {
      fn.apply(this, args)
      lastTime = Date.now()
      inThrottle = true
    } else {
      clearTimeout(lastFn)
      lastFn = setTimeout(() => {
        if (Date.now() - lastTime >= wait) {
          fn.apply(this, args)
          lastTime = Date.now()
        }
      }, Math.max(wait - (Date.now - lastTime), 0))
    }
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant