节流的运用场景和实现

84 阅读2分钟

节流的运用场景和实现

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情

前言

节流函数是规定一个单位时间,在这段时间内多次触发的情况都不会生效。

假设我们设置1s执行一次,那么在这一秒钟只会执行一次,当走到下一秒的时候才会触发。

实例

常见的网页中我们或许需要监听网页的滚动,但是这个事件在滚动的过程中触发的过于频繁,为了减少触发次数,就需要使用节流函数来进行限制,使其只在一段时间内执行一次

代码实现

一个普通的节流函数,需要传递节流的函数延迟时间,在我们执行函数的时候开启一个定时器,并创建一个 canRun 变量,当定时器执行完毕之后才能开启函数的再次执行,代码如下:

function throttle(func, wait = 500) {
  let canRun = true
  if (typeof func !== 'function')
    throw new TypeError(`Expected a function, got ${typeof func}`)

  return function () {
    const that = this
    const args = arguments
    if (canRun) {
      canRun = false

      setTimeout(() => {
        func.apply(that, args)
        canRun = true
      }, wait)
    }
  }
}

上面只是简单的实现了一个节流函数,从代码可以看出,函数执行之后不会立即执行我们需要执行的函数,如果我们想要立即执行该如何处理呢?

立即执行

我们只需要传递一个控制变量,进入的时候立即执行函数就可以完成该功能。

function throttle(func, wait = 500, options) {
  let canRun = true
  const leading = options.leading ?? true // 立即执行
  if (typeof func !== 'function')
    throw new TypeError(`Expected a function, got ${typeof func}`)

  return function () {
    const that = this
    const args = arguments
    if (canRun) {
      canRun = false
      clearTimeout(timer)

      leading && func.apply(that, args)
      setTimeout(() => {
        !leading && func.apply(that, args)
        canRun = true
      }, wait)
    }
  }
}

加大难度,如何确保节流函数最后一次一定执行呢?

尾执行

首先需要明白一件事情,想要确保最后一次执行,那么在点击的时候 canRun一定是false,在这个时候可以开启 else 分支,开启一个定时器,来确保最后一次执行。

当然,如果不是最后一次我们需要清除这个定时器,在进入函数的时候我们就要清除这个定时器,把下一次定位最后一次。

function throttle(func, wait, options) {
  let canRun = true
  const leading = options.leading ?? true // 立即执行
  const trailing = options.trailing ?? true // 尾执行
  let timer = null
  if (typeof func !== 'function')
    throw new TypeError(`Expected a function, got ${typeof func}`)

  return function () {
    const that = this
    const args = arguments
    clearTimeout(timer)

    if (canRun) {
      canRun = false

      leading && func.apply(that, args)
      setTimeout(() => {
        !leading && func.apply(that, args)
        canRun = true
      }, wait)
    }
    else {
      if (trailing) {
        timer = setTimeout(() => {
          func.apply(that, args)
        }, wait)
      }
    }
  }
}