Skip to content

Commit

Permalink
fix: debounce cancel should clear pending timeout
Browse files Browse the repository at this point in the history
  • Loading branch information
adamhamlin committed Jul 13, 2024
1 parent d7bea7a commit 6065256
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 17 deletions.
2 changes: 1 addition & 1 deletion docs/curry/debounce.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Source Invocations: - - - - - - - - - - x - - - - - - - - - - - - - - - - - x -

### Cancel

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

```ts
const debounced = _.debounce({ delay: 100 }, api.feed.refresh)
Expand Down
16 changes: 6 additions & 10 deletions src/curry/debounce.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,24 +41,20 @@ export function debounce<TArgs extends any[]>(
func: (...args: TArgs) => any,
): DebounceFunction<TArgs> {
let timer: unknown = undefined
let active = true

const debounced: DebounceFunction<TArgs> = (...args: TArgs) => {
if (active) {
clearTimeout(timer)
timer = setTimeout(() => {
active && func(...args)
timer = undefined
}, delay)
} else {
clearTimeout(timer)
timer = setTimeout(() => {
func(...args)
}
timer = undefined
}, delay)
}
debounced.isPending = () => {
return timer !== undefined
}
debounced.cancel = () => {
active = false
clearTimeout(timer)
timer = undefined
}
debounced.flush = (...args: TArgs) => func(...args)

Expand Down
21 changes: 15 additions & 6 deletions tests/curry/debounce.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,23 @@ describe('debounce', () => {
expect(mockFunc).toHaveBeenCalledTimes(1)
})

test('does not debounce after cancel is called', () => {
runFunc3Times()
test('does not execute if cancel called before timeout', () => {
func()
expect(func.isPending()).toBe(true);
func.cancel()
expect(func.isPending()).toBe(false);
vi.advanceTimersByTime(delay + 10)
expect(mockFunc).toHaveBeenCalledTimes(0)
})

test('continues to debounce after cancel is called', () => {
func()
func.cancel()
runFunc3Times()
expect(mockFunc).toHaveBeenCalledTimes(3)
runFunc3Times()
expect(mockFunc).toHaveBeenCalledTimes(6)
vi.advanceTimersByTime(delay + 10)
expect(mockFunc).toHaveBeenCalledTimes(0)
func()
vi.advanceTimersByTime(delay + 10)
expect(mockFunc).toHaveBeenCalledTimes(1)
})

test('executes the function immediately when the flush method is called', () => {
Expand Down

0 comments on commit 6065256

Please sign in to comment.