Skip to content

Commit

Permalink
Feature/debounce with cancel and flush (#118)
Browse files Browse the repository at this point in the history
* test(curry - debounce): handle with cancel

* test(curry - debounce): handle with flush method and side effects

* feat(curry - debounce): inplements *.cancel and *.flush

* test(curry - debounce): show case if cancel past invocations

* refactor(curry - debounce): improves typing of timer

* docs(curry - debounce): talk about cancel and flush methods

Co-authored-by: marlon Felipe Passos <[email protected]>
  • Loading branch information
MarlonPassos-git and marlon Felipe Passos authored Dec 11, 2022
1 parent 088070c commit 8fdc2f7
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 21 deletions.
17 changes: 14 additions & 3 deletions cdn/radash.esm.js
Original file line number Diff line number Diff line change
Expand Up @@ -492,11 +492,22 @@ const memo = (func, {
return memoize({}, func, key, ttl);
};
const debounce = ({ delay }, func) => {
let timer = null;
let timer = void 0;
let active = true;
const debounced = (...args) => {
clearTimeout(timer);
timer = setTimeout(() => func(...args), delay);
if (active) {
clearTimeout(timer);
timer = setTimeout(() => {
active && func(...args);
}, delay);
} else {
func(...args);
}
};
debounced.cancel = () => {
active = false;
};
debounced.flush = (...args) => func(...args);
return debounced;
};
const throttle = ({ interval }, func) => {
Expand Down
17 changes: 14 additions & 3 deletions cdn/radash.js
Original file line number Diff line number Diff line change
Expand Up @@ -495,11 +495,22 @@ var radash = (function (exports) {
return memoize({}, func, key, ttl);
};
const debounce = ({ delay }, func) => {
let timer = null;
let timer = void 0;
let active = true;
const debounced = (...args) => {
clearTimeout(timer);
timer = setTimeout(() => func(...args), delay);
if (active) {
clearTimeout(timer);
timer = setTimeout(() => {
active && func(...args);
}, delay);
} else {
func(...args);
}
};
debounced.cancel = () => {
active = false;
};
debounced.flush = (...args) => func(...args);
return debounced;
};
const throttle = ({ interval }, func) => {
Expand Down
2 changes: 1 addition & 1 deletion cdn/radash.min.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "radash",
"version": "10.0.0",
"version": "10.1.0",
"description": "Functional utility library - modern, simple, typed, powerful",
"main": "dist/cjs/index.cjs",
"module": "dist/esm/index.mjs",
Expand Down Expand Up @@ -48,4 +48,4 @@
"engines": {
"node": ">=14.18.0"
}
}
}
43 changes: 36 additions & 7 deletions src/curry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,21 +91,50 @@ export const memo = <TFunc extends Function>(
return memoize({}, func as any, key, ttl) as any as TFunc
}

export type DebounceFunction<TArgs extends any[]> = {
(...args: TArgs): void
/**
* Cancels the debounced function
*/
cancel(): void
/**
* Runs the debounced function immediately
*/
flush(...args: TArgs): void
}

/**
* 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
* 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
*/
export const debounce = <TArgs extends any[]>(
{ delay }: { delay: number },
func: (...args: TArgs) => any
): ((...args: TArgs) => void) => {
let timer: any = null
const debounced = (...args: TArgs) => {
clearTimeout(timer)
timer = setTimeout(() => func(...args), delay)
) => {
let timer: NodeJS.Timeout | undefined = undefined
let active = true

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

return debounced
}

/**
Expand Down
58 changes: 53 additions & 5 deletions src/tests/curry.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { assert } from 'chai'
import * as _ from '..'
import { DebounceFunction } from '../curry'

describe('curry module', () => {
describe('compose function', () => {
Expand Down Expand Up @@ -147,15 +148,62 @@ describe('curry module', () => {
})

describe('debounce function', () => {
test('only executes once when called rapidly', async () => {
let calls = 0
const func = _.debounce({ delay: 600 }, () => calls++)
let func: DebounceFunction<any>
const mockFunc = jest.fn()
const runFunc3Times = () => {
func()
func()
func()
assert.equal(calls, 0)
}

beforeEach(() => {
func = _.debounce({ delay: 600 }, mockFunc)
})

afterEach(() => {
jest.clearAllMocks()
})

test('only executes once when called rapidly', async () => {
runFunc3Times()
expect(mockFunc).toHaveBeenCalledTimes(0)
await _.sleep(610)
assert.equal(calls, 1)
expect(mockFunc).toHaveBeenCalledTimes(1)
})

test('does not debounce after cancel is called', () => {
runFunc3Times()
expect(mockFunc).toHaveBeenCalledTimes(0)
func.cancel()
runFunc3Times()
expect(mockFunc).toHaveBeenCalledTimes(3)
runFunc3Times()
expect(mockFunc).toHaveBeenCalledTimes(6)
})

test('when we call the flush method it should execute the function immediately', () => {
func.flush()
expect(mockFunc).toHaveBeenCalledTimes(1)
})

test('continues to debounce after flush is called', async () => {
runFunc3Times()
expect(mockFunc).toHaveBeenCalledTimes(0)
func.flush()
expect(mockFunc).toHaveBeenCalledTimes(1)
func()
expect(mockFunc).toHaveBeenCalledTimes(1)
await _.sleep(610)
expect(mockFunc).toHaveBeenCalledTimes(2)
func.flush()
expect(mockFunc).toHaveBeenCalledTimes(3)
})

test('cancels all pending invocations when cancel is called', async () => {
func()
func.cancel()
await _.sleep(610)
expect(mockFunc).toHaveBeenCalledTimes(0)
})
})

Expand Down

0 comments on commit 8fdc2f7

Please sign in to comment.