debounce

一定时间连续触发的事件只在最后执行一次
注意事项:
1. 修正 this
2. 第一次是否需要立即执行

export function debounce(fn: Function, delay: number = 0, immediate = false) {
  let timer: NodeJS.Timeout | null

  return function (...args: any[]) {
    if (timer) clearTimeout(timer)

    if (immediate && !timer) {
      fn.apply(this, args)
    }

    timer = setTimeout(() => {
      fn.apply(this, args)
      timer = null
    }, delay)
  }
}

一段时间内只执行一次
setTimeout 版本
1. 事件触发不立即执行
2. 事件触发停止后还会执行一次
3. 异步执行

export function throttleTimer(fn: Function, delay: number = 0) {
  let timer: NodeJS.Timeout | null

  return function (...args: any[]) {
    if (timer) return
    timer = setTimeout(() => {
      fn.apply(this, args)
      timer = null
    }, delay)
  }
}

时间戳版本
1. 事件触发立即执行
2. 事件触发停止后不会再执行
3. 同步执行

export function throttleTimestamp(fn: Function, delay: number = 0) {
  let previous = 0

  return function (...args: any[]) {
    const now = Date.now()

    if (now - previous >= delay) {
      fn.apply(this, args)
      previous = now
    }
  }
}

综合版
1. 事件触发立即执行
2. 事件触发停止后再执行一次

export function throttle(fn: Function, delay: number = 0) {
  let timer: NodeJS.Timeout | null
  let previous = 0

  return function (...args: any[]) {
    const now = Date.now()
    const remaining = delay - (now - previous)
    if (remaining <= 0) {
      fn.apply(this, args)
      previous = now
    } else {
      // 保证只执行最后一个 timer
      if (timer) clearTimeout(timer)
      timer = setTimeout(() => {
        fn.apply(this, args)
        previous = now
        timer = null
      }, remaining)
    }
  }
}