lodash防抖函数解析

472 阅读3分钟
// func是要去执行的函数,wait是需要延迟时间,options是一个对象
// options.leading 是否在超时前调用
// options.maxWait 函数延迟调用的最大时间
// options.trailing	是否在超时后调用
function debounce(func, wait, options) {
  let lastArgs,		// 上次调用的参数
    lastThis,	// 上次调用的this
    maxWait,		// 函数延迟调用的最大时间
    result,	// 返回结果
    timerId,
    lastCallTime // 上次调用debounced时间,出发事件,不一定会调用func

  let lastInvokeTime = 0	// 上次调用func事件,成功执行时间
  let leading = false	// 是否在超时之前调用
  let maxing = false	// 是否传入最大超时时间
  let trailing = true	// 是否超时后调用

  // Bypass `requestAnimationFrame` by explicitly setting `wait=0`.
  const useRAF = (!wait && wait !== 0 && typeof root.requestAnimationFrame === 'function')

  if (typeof func !== 'function') {
    throw new TypeError('Expected a function')
  }
  wait = +wait || 0	// 转变wait的数据类型,转变为number类型
  if (isObject(options)) {
    leading = !!options.leading
    maxing = 'maxWait' in options
    maxWait = maxing ? Math.max(+options.maxWait || 0, wait) : maxWait
    trailing = 'trailing' in options ? !!options.trailing : trailing
  }
	// 参数为当前时间
  function invokeFunc(time) {
    const args = lastArgs
    const thisArg = lastThis

    lastArgs = lastThis = undefined
    lastInvokeTime = time	// 将上次的调用时间重置为当前时间
    result = func.apply(thisArg, args)	// 执行传入
    return result
  }

  function startTimer(pendingFunc, wait) {
    // 对特殊环境的一个处理
    if (useRAF) {
      root.cancelAnimationFrame(timerId)
      return root.requestAnimationFrame(pendingFunc)
    }
    // 
    return setTimeout(pendingFunc, wait)
  }

  function cancelTimer(id) {
    if (useRAF) {
      return root.cancelAnimationFrame(id)
    }
    clearTimeout(id)
  }
	// 超时之前调用
  function leadingEdge(time) {
    // Reset any `maxWait` timer.
    lastInvokeTime = time
    // Start the timer for the trailing edge.
    timerId = startTimer(timerExpired, wait)
    // Invoke the leading edge.
    return leading ? invokeFunc(time) : result
  }
	// 设置还需要等待的时间
  function remainingWait(time) {
    const timeSinceLastCall = time - lastCallTime	// 距离上次debounced函数触发的时间
    const timeSinceLastInvoke = time - lastInvokeTime	// 距离上次func函数被执行的时间
    const timeWaiting = wait - timeSinceLastCall // 还需要等待的时间,计算出下一次trailing的位置
		//maxing就是options.maxWait
    return maxing
      ? Math.min(timeWaiting, maxWait - timeSinceLastInvoke)	// 如果设置了maxWait,比较还需等待时间和下一次最大延迟需要时间,最小值最为下一次函数要执行的时间
      : timeWaiting // 如果没有设置maxWait	// 正常还需要等待时间
  }
	// 根据时间判断是否可以调用
  function shouldInvoke(time) {
    const timeSinceLastCall = time - lastCallTime		// lastCallTime为上次调用的时间,和当前的时间的存储形成一个前后的时间差(距离上次debonced触发的时间)
    const timeSinceLastInvoke = time - lastInvokeTime	// 距离上次执行func的时间

    // Either this is the first call, activity has stopped and we're at the
    // trailing edge, the system time has gone backwards and we're treating
    // it as the trailing edge, or we've hit the `maxWait` limit.
    return (lastCallTime === undefined  // 首次执行,第一次被调用
            || (timeSinceLastCall >= wait) 	// 距离上次调用的时间超过了wait设置的时间
            || (timeSinceLastCall < 0) // 这儿不太清楚为啥时间会小于0
            || (maxing && timeSinceLastInvoke >= maxWait)	// 超过了最大的等待时间
           )
  }
	// 定时器的回调函数,用来判断是否执行func
  function timerExpired() {
    const time = Date.now()
    // 判断是否可以调用函数,如果不行就重启定时器
    if (shouldInvoke(time)) {
      return trailingEdge(time)
    }
    // 重启定时器,保证下次时延的末尾触发
    timerId = startTimer(timerExpired, remainingWait(time))
  }
	// 超时后调用
  function trailingEdge(time) {
    timerId = undefined

    // 有lastArgs才执行,意味着抖过一次才会去执行
    if (trailing && lastArgs) {
      return invokeFunc(time)
    }
    lastArgs = lastThis = undefined
    return result
  }

  function cancel() {
    if (timerId !== undefined) {
      cancelTimer(timerId)
    }
    lastInvokeTime = 0
    lastArgs = lastCallTime = lastThis = timerId = undefined
  }

  function flush() {
    return timerId === undefined ? result : trailingEdge(Date.now())
  }

  function pending() {
    return timerId !== undefined
  }

  function debounced(...args) {
    const time = Date.now()	// 获取到当前时间
    const isInvoking = shouldInvoke(time)	// 判断是否可以调用

    lastArgs = args
    lastThis = this
    lastCallTime = time	// 调用后将时间存储起来,下次调用这个时间就是上次调用的时间
		
    // 正式调用
    if (isInvoking) {
      // 超时之前调用,在未到达时间前为undefined
      if (timerId === undefined) {
        return leadingEdge(lastCallTime)
      }
      // 最大延迟调用
      if (maxing) {
        // Handle invocations in a tight loop.
        timerId = startTimer(timerExpired, wait)
        return invokeFunc(lastCallTime)
      }
    }
    // 都没有的正常调用
    if (timerId === undefined) {
      timerId = startTimer(timerExpired, wait)
    }
    return result
  }
  debounced.cancel = cancel
  debounced.flush = flush
  debounced.pending = pending
  return debounced	// 调用debounced函数,调用入口
}

export default debounce