在实际的开发中,我们通常会期望在首次和末次都执行一次,这时候我们就需要将节流和防抖结合起来。

throttleAndDebounce

  1. function throttleAndDebounce(fn: () => void, delay: number) {
  2. let timeout: number
  3. let called: boolean
  4. let ctx: any
  5. return function(...args: []) {
  6. ctx = this
  7. if (timeout) {
  8. clearTimeout(timeout)
  9. }
  10. if (!called) {
  11. fn.apply(ctx, args)
  12. called = true
  13. timeout = setTimeout(() => {
  14. called = false
  15. }, delay)
  16. } else {
  17. timeout = setTimeout(() => fn.apply(ctx, args), delay)
  18. }
  19. }
  20. }

为了实现对应的效果,我们需要使用闭包来保存两个参数,定时器的 timeoutid 和当前的执行状态 called。当执行函数的时候,每次都会检查是否存在 timeoutid,若存在则将其清理掉。然后再判断当前的执行状态,当在当前周期内执行过一次之后就将 called置为 true,并生成一个计时器,在特定时间之后再将其重置为 false。若在某个时间停止执行之后,还会保留一个计时器,即 16 行所写,这样就会在最后一次触发特定时间之后完成最后的触发操作。

debounce

防抖是指对持续触发的函数进行延迟执行,当触发之后间隔一点时间没有触发时函数才会执行一次。因为这样的特性,通常情况下第一次触发不会直接执行,而最后一次触发会在间隔一定时间之后进行执行。

  1. function debounce(fn: () => void, delay: number) {
  2. let timeout: number
  3. return function (...args) {
  4. const ctx = this
  5. clearTimeout(timeout)
  6. timeout = setTimeout(() => {
  7. fn.apply(ctx, args)
  8. })
  9. }
  10. }
  1. function debounce(fn: Function, wait: number, immediate: boolean) {
  2. let timeout
  3. return function (...args: any[]) {
  4. const ctx = this
  5. const callNow = immediate && !timeout
  6. const later = function () {
  7. timeout = null
  8. if (!immediate) fn.apply(ctx, args)
  9. }
  10. clearTimeout(timeout)
  11. timeout = setTimeout(later, wait)
  12. if (callNow) fn.apply(ctx, args)
  13. }
  14. }

函数会在第一次触发的时候执行,间隔一定时间之后,再次触发也是最后先执行。

throttle

节流是当函数不断进行触发的时候,只有在经过特定的时间间隔才会执行一次。

  1. function throttle(fn: () => void, delay: number) {
  2. let lastTime = 0
  3. return function (...args) {
  4. let now = +new Date()
  5. if (now - lastTime > delay) {
  6. lastTime = now
  7. fn.apply(this, args)
  8. }
  9. }
  10. }
  1. function throttle(fn: Function, delay: number) {
  2. let timerId = null
  3. return function (...args) {
  4. const ctx = this
  5. if (!timerId) {
  6. timerId = setTimeout(() => {
  7. timerId = null
  8. fn.apply(ctx, args)
  9. }, delay)
  10. }
  11. }
  12. }

节流是第一次就会触发,然后再在固定时间内触发一次,当停止之后最后一次也不会触发。