Skip to content

Commit

Permalink
feat(curry): add leading option for function debounce
Browse files Browse the repository at this point in the history
test(curry): add test for `leading` option
doc(curry): add description and example of `leading` option
  • Loading branch information
LynnSha1ng committed Mar 19, 2024
1 parent 069b26c commit 0f5c6ab
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 8 deletions.
6 changes: 5 additions & 1 deletion cdn/radash.esm.js
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,7 @@ const memoize = (cache, func, keyFunc, ttl) => {
const memo = (func, options = {}) => {
return memoize({}, func, options.key ?? null, options.ttl ?? null);
};
const debounce = ({ delay }, func) => {
const debounce = ({ delay, leading = false }, func) => {
let timer = void 0;
let active = true;
const debounced = (...args) => {
Expand All @@ -543,6 +543,10 @@ const debounce = ({ delay }, func) => {
active && func(...args);
timer = void 0;
}, delay);
if (leading) {
func(...args);
leading = false;
}
} else {
func(...args);
}
Expand Down
6 changes: 5 additions & 1 deletion cdn/radash.js
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ var radash = (function (exports) {
const memo = (func, options = {}) => {
return memoize({}, func, options.key ?? null, options.ttl ?? null);
};
const debounce = ({ delay }, func) => {
const debounce = ({ delay, leading = false }, func) => {
let timer = void 0;
let active = true;
const debounced = (...args) => {
Expand All @@ -546,6 +546,10 @@ var radash = (function (exports) {
active && func(...args);
timer = void 0;
}, delay);
if (leading) {
func(...args);
leading = false;
}
} else {
func(...args);
}
Expand Down
2 changes: 1 addition & 1 deletion cdn/radash.min.js

Large diffs are not rendered by default.

30 changes: 27 additions & 3 deletions docs/curry/debounce.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ description: Create a debounced callback function

## Basic usage

Debounce accepts an options object with a `delay` and a source function to call
Debounce accepts an options object with `delay`, `leading`, 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.
in invoking the source reset the delay, pushing off the next invocation. The `leading`
option decides whether the source function is called on the first invocation of the debounce
function or not.

```ts
import { debounce } from 'radash'

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

Expand All @@ -33,6 +35,28 @@ debounce Invocations: x x x x - - - - - - - - x x x x x x x x x x - - - - - - -
Source Invocations: - - - - - - - - - - x - - - - - - - - - - - - - - - - - x - - - - -
```

## Leading

The `leading` option, `false` by default, will call the source function immediately
the first time the debounce function is invoked when set to `true`.

```ts
// hide the header when scroll down and show it when scroll up.

const header = document.getElementById('page-header')
let lastY = 0

window.addEventListener(
'scroll',
debounce({ delay: 100, leading: true }, () => {
header.style.transform = `translateY(${
window.scrollY - lastY > 0 ? '-100%' : '0'
})`
lastY = window.scrollY
})
)
```

### Cancel

The function returned by `debounce` has a `cancel` method that when called will permanently stop the source function from being debounced.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "radash",
"version": "12.1.0",
"version": "12.1.1",
"description": "Functional utility library - modern, simple, typed, powerful",
"main": "dist/cjs/index.cjs",
"module": "dist/esm/index.mjs",
Expand Down
19 changes: 18 additions & 1 deletion src/curry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,19 @@ export type DebounceFunction<TArgs extends any[]> = {
flush(...args: TArgs): void
}

export type DebounceConfig = {
/**
* The time in milliseconds to wait before calling the
* source function
*/
delay: number;
/**
* whether the source function will be called on the first
* invocation of the debounce function. `false` by default
*/
leading?: boolean;
}

export type ThrottledFunction<TArgs extends any[]> = {
(...args: TArgs): void
/**
Expand All @@ -512,7 +525,7 @@ export type ThrottledFunction<TArgs extends any[]> = {
* method to invoke them immediately
*/
export const debounce = <TArgs extends any[]>(
{ delay }: { delay: number },
{ delay, leading = false }: DebounceConfig,
func: (...args: TArgs) => any
) => {
let timer: NodeJS.Timeout | undefined = undefined
Expand All @@ -525,6 +538,10 @@ export const debounce = <TArgs extends any[]>(
active && func(...args)
timer = undefined
}, delay)
if (leading) {
func(...args)
leading = false
}
} else {
func(...args)
}
Expand Down
8 changes: 8 additions & 0 deletions src/tests/curry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,14 @@ describe('curry module', () => {
expect(mockFunc).toHaveBeenCalledTimes(1)
})

test('executes the function immediately on the first invocation of the debounce function when set `leading` to true', async () => {
func = _.debounce({ delay: 600, leading: true }, mockFunc)
runFunc3Times()
expect(mockFunc).toHaveBeenCalledTimes(1)
await _.sleep(610)
expect(mockFunc).toHaveBeenCalledTimes(2)
})

test('does not debounce after cancel is called', () => {
runFunc3Times()
expect(mockFunc).toHaveBeenCalledTimes(0)
Expand Down

0 comments on commit 0f5c6ab

Please sign in to comment.