Skip to content

Commit

Permalink
rAF as default for debounce/throtte (lodash#3560) (lodash#3567)
Browse files Browse the repository at this point in the history
Uses rAF by default for debounce/throtte unless `wait` is specified. (lodash#3560)
  • Loading branch information
mattlubner authored and jdalton committed Jan 1, 2018
1 parent fa73d46 commit d985dbf
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 20 deletions.
50 changes: 37 additions & 13 deletions debounce.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import isObject from './isObject.js'
import root from './.internal/root.js'

/**
* Creates a debounced function that delays invoking `func` until after `wait`
* milliseconds have elapsed since the last time the debounced function was
* invoked. The debounced function comes with a `cancel` method to cancel
* delayed `func` invocations and a `flush` method to immediately invoke them.
* Provide `options` to indicate whether `func` should be invoked on the
* leading and/or trailing edge of the `wait` timeout. The `func` is invoked
* with the last arguments provided to the debounced function. Subsequent
* calls to the debounced function return the result of the last `func`
* invocation.
* invoked, or until the next browser frame is drawn. The debounced function
* comes with a `cancel` method to cancel delayed `func` invocations and a
* `flush` method to immediately invoke them. Provide `options` to indicate
* whether `func` should be invoked on the leading and/or trailing edge of the
* `wait` timeout. The `func` is invoked with the last arguments provided to the
* debounced function. Subsequent calls to the debounced function return the
* result of the last `func` invocation.
*
* **Note:** If `leading` and `trailing` options are `true`, `func` is
* invoked on the trailing edge of the timeout only if the debounced function
Expand All @@ -18,13 +19,19 @@ import isObject from './isObject.js'
* If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
* until the next tick, similar to `setTimeout` with a timeout of `0`.
*
* If `wait` is omitted in an environment with `requestAnimationFrame`, `func`
* invocation will be deferred until the next frame is drawn (typically about
* 16ms).
*
* See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
* for details over the differences between `debounce` and `throttle`.
*
* @since 0.1.0
* @category Function
* @param {Function} func The function to debounce.
* @param {number} [wait=0] The number of milliseconds to delay.
* @param {number} [wait=0]
* The number of milliseconds to delay; if omitted, `requestAnimationFrame` is
* used (if available).
* @param {Object} [options={}] The options object.
* @param {boolean} [options.leading=false]
* Specify invoking on the leading edge of the timeout.
Expand Down Expand Up @@ -68,6 +75,9 @@ function debounce(func, wait, options) {
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')
}
Expand All @@ -89,11 +99,25 @@ function debounce(func, wait, options) {
return result
}

function startTimer(pendingFunc, wait) {
if (useRAF) {
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 = setTimeout(timerExpired, wait)
timerId = startTimer(timerExpired, wait)
// Invoke the leading edge.
return leading ? invokeFunc(time) : result
}
Expand Down Expand Up @@ -125,7 +149,7 @@ function debounce(func, wait, options) {
return trailingEdge(time)
}
// Restart the timer.
timerId = setTimeout(timerExpired, remainingWait(time))
timerId = startTimer(timerExpired, remainingWait(time))
}

function trailingEdge(time) {
Expand All @@ -142,7 +166,7 @@ function debounce(func, wait, options) {

function cancel() {
if (timerId !== undefined) {
clearTimeout(timerId)
cancelTimer(timerId)
}
lastInvokeTime = 0
lastArgs = lastCallTime = lastThis = timerId = undefined
Expand Down Expand Up @@ -170,12 +194,12 @@ function debounce(func, wait, options) {
}
if (maxing) {
// Handle invocations in a tight loop.
timerId = setTimeout(timerExpired, wait)
timerId = startTimer(timerExpired, wait)
return invokeFunc(lastCallTime)
}
}
if (timerId === undefined) {
timerId = setTimeout(timerExpired, wait)
timerId = startTimer(timerExpired, wait)
}
return result
}
Expand Down
20 changes: 13 additions & 7 deletions throttle.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import isObject from './isObject.js'

/**
* Creates a throttled function that only invokes `func` at most once per
* every `wait` milliseconds. The throttled function comes with a `cancel`
* method to cancel delayed `func` invocations and a `flush` method to
* immediately invoke them. Provide `options` to indicate whether `func`
* should be invoked on the leading and/or trailing edge of the `wait`
* timeout. The `func` is invoked with the last arguments provided to the
* every `wait` milliseconds (or once per browser frame). The throttled function
* comes with a `cancel` method to cancel delayed `func` invocations and a
* `flush` method to immediately invoke them. Provide `options` to indicate
* whether `func` should be invoked on the leading and/or trailing edge of the
* `wait` timeout. The `func` is invoked with the last arguments provided to the
* throttled function. Subsequent calls to the throttled function return the
* result of the last `func` invocation.
*
Expand All @@ -16,15 +16,21 @@ import isObject from './isObject.js'
* is invoked more than once during the `wait` timeout.
*
* If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
* until to the next tick, similar to `setTimeout` with a timeout of `0`.
* until the next tick, similar to `setTimeout` with a timeout of `0`.
*
* If `wait` is omitted in an environment with `requestAnimationFrame`, `func`
* invocation will be deferred until the next frame is drawn (typically about
* 16ms).
*
* See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
* for details over the differences between `throttle` and `debounce`.
*
* @since 0.1.0
* @category Function
* @param {Function} func The function to throttle.
* @param {number} [wait=0] The number of milliseconds to throttle invocations to.
* @param {number} [wait=0]
* The number of milliseconds to throttle invocations to; if omitted,
* `requestAnimationFrame` is used (if available).
* @param {Object} [options={}] The options object.
* @param {boolean} [options.leading=true]
* Specify invoking on the leading edge of the timeout.
Expand Down

0 comments on commit d985dbf

Please sign in to comment.