Skip to content

Commit

Permalink
docs: rewrite debounce docs
Browse files Browse the repository at this point in the history
  • Loading branch information
aleclarson committed Aug 15, 2024
1 parent 942057e commit a5b766c
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 69 deletions.
96 changes: 53 additions & 43 deletions docs/curry/debounce.mdx
Original file line number Diff line number Diff line change
@@ -1,78 +1,88 @@
---
title: debounce
description: Create a debounced callback function
description: Delay a function until after a specified time has elapsed since the last call
---

### Usage

Debounce accepts an options object with a `delay` and a source function to call
when invoked. When the returned function is invoked it will only call the source
function after the `delay` milliseconds of time has passed. Calls that don't result
in invoking the source reset the delay, pushing off the next invocation.
The `debounce` function helps manage frequent function calls efficiently. It requires two inputs: a `delay` time (in milliseconds) and a callback. When you use the function returned by `debounce` (a.k.a. the “debounced function”), it doesn't immediately run your callback. Instead, it waits for the specified `delay`.

If called again during this waiting period, it resets the timer. Your source function only runs after the full `delay` passes without interruption. This is useful for handling rapid events like keystrokes, ensuring your code responds only after a pause in activity.

```ts
import * as _ from 'radashi'

const makeSearchRequest = event => {
api.movies.search(event.target.value)
}

input.addEventListener('change', _.debounce({ delay: 100 }, makeSearchRequest))
// Send a search request to the API server when the user stops typing
// for at least 100ms.
input.addEventListener(
'change',
_.debounce({ delay: 100 }, (event: InputEvent) => {
api.movies.search(event.target.value)
}),
)
```

### Timing
## Options

A visual of the debounce behavior when `delay` is `100`. The debounce function
returned by `debounce` can be called every millisecond but it will only call
the given callback after `delay` milliseconds have passed.
### leading

```sh
Time: 0ms - - - - 100ms - - - - 200ms - - - - 300ms - - - - 400ms - - - -
debounce Invocations: x x x x - - - - - - - - x x x x x x x x x x - - - - - - - - - - - -
Source Invocations: - - - - - - - - - - x - - - - - - - - - - - - - - - - - x - - - - -
```
When the `leading` option is `true`, your callback is invoked immediately the very first time the debounced function is called. After that, the debounced function works as if `leading` was `false`.

The `leading` option decides whether the source function is called on the first
invocation of the throttle function or not.
```ts
const myDebouncedFunc = _.debounce({ delay: 100, leading: true }, x => {
console.log(x)
})

```sh
Time: 0ms - - - - 100ms - - - - 200ms - - - - 300ms - - - - 400ms - - - -
debounce Invocations: x x x x - - - - - - - - x x x x x x x x x x - - - - - - - - - - - -
Source Invocations: x - - - - - - - - - x - - - - - - - - - - - - - - - - - x - - - - -
myDebouncedFunc(0) // Logs "0" immediately
myDebouncedFunc(1) // Never logs
myDebouncedFunc(2) // Logs "2" about 100ms later
```

### Cancel
## Methods

The function returned by `debounce` has a `cancel` method that when called will permanently stop the source function from being debounced.
### cancel

```ts
const debounced = _.debounce({ delay: 100 }, api.feed.refresh)
The `cancel` method of the debounced function does two things:

// ... sometime later
1. It cancels any pending invocations of the debounced function.
2. It permanently disables the debouncing behavior. All future invocations of the debounced function will immediately invoke your callback.

debounced.cancel()
```ts
const myDebouncedFunc = _.debounce({ delay: 100 }, x => {
console.log(x)
})

myDebouncedFunc(0) // Never logs
myDebouncedFunc(1) // Never logs
myDebouncedFunc.cancel()
myDebouncedFunc(2) // Logs "2" immediately
```

### Flush
### flush

The function returned by `debounce` has a `flush` method that when called will directly invoke the source function.
The `flush` method will immediately invoke your callback, regardless of whether the debounced function is currently pending.

```ts
const debounced = _.debounce({ delay: 100 }, api.feed.refresh)

// ... sometime later
const myDebouncedFunc = _.debounce({ delay: 100 }, x => {
console.log(x)
})

debounced.flush(event)
myDebouncedFunc(0) // Logs "0" about 100ms later
myDebouncedFunc.flush(1) // Logs "1" immediately
```

### isPending

The function returned by `debounce` has a `isPending` method that when called will return if there is any pending invocation the source function.
The `isPending` method returns `true` if there is any pending invocation of the debounced function.

```ts
const debounced = _.debounce({ delay: 100 }, api.feed.refresh)

// ... sometime later

debounced.isPending()
const myDebouncedFunc = _.debounce({ delay: 100 }, x => {
console.log(x)
})

myDebouncedFunc(0) // Logs "0" about 100ms later
myDebouncedFunc.isPending() // => true
setTimeout(() => {
myDebouncedFunc.isPending() // => false
}, 100)
```
54 changes: 28 additions & 26 deletions src/curry/debounce.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,52 +4,54 @@ declare const clearTimeout: (timer: unknown) => void
export type DebounceFunction<TArgs extends any[]> = {
(...args: TArgs): void
/**
* Cancels the debounced function
* When called, future invocations of the debounced function are
* no longer delayed and are instead executed immediately.
*/
cancel(): void
/**
* Checks if there is any invocation debounced
* Returns `true` if the underlying function is scheduled to be
* called once the delay has passed.
*/
isPending(): boolean
/**
* Runs the debounced function immediately
* Invoke the underlying function immediately.
*/
flush(...args: TArgs): void
}

export interface DebounceOptions {
delay: number
/**
* When true, your callback is invoked immediately the very first
* time the debounced function is called. After that, the debounced
* function works as if `leading` was `false`.
*
* @default false
*/
leading?: boolean
}

/**
* Given a delay and a function returns a new function that will only
* call the source function after delay milliseconds have passed
* without any invocations.
* Returns a new function that will only call your callback after
* `delay` milliseconds have passed without any invocations.
*
* The debounce function comes with a `cancel` method to cancel
* delayed `func` invocations and a `flush` method to invoke them
* immediately.
* The debounced function has a few methods, such as `cancel`,
* `isPending`, and `flush`.
*
* @see https://radashi-org.github.io/reference/curry/debounce
* @example
* ```ts
* const myDebouncedFunc = debounce({ delay: 1000 }, (x) => console.log(x))
* const myDebouncedFunc = debounce({ delay: 1000 }, (x) => {
* console.log(x)
* })
*
* myDebouncedFunc(0)
* myDebouncedFunc(1)
* // Logs 1, but not 0
* myDebouncedFunc(0) // Nothing happens
* myDebouncedFunc(1) // Nothing happens
* // Logs "1" about 1 second after the last invocation
* ```
*/
export function debounce<TArgs extends any[]>(
{
delay,
leading = false,
}: {
delay: number
/**
* Decides whether the source function is called on the first
* invocation of the throttle function or not
*
* @default false
*/
leading?: boolean
},
{ delay, leading }: DebounceOptions,
func: (...args: TArgs) => any,
): DebounceFunction<TArgs> {
let timer: unknown = undefined
Expand Down

0 comments on commit a5b766c

Please sign in to comment.