Skip to content

Commit

Permalink
feat: trigger edge param for debounce
Browse files Browse the repository at this point in the history
  • Loading branch information
Sv443 committed Apr 2, 2024
1 parent b1214a9 commit a11ed77
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 11 deletions.
5 changes: 5 additions & 0 deletions .changeset/grumpy-snakes-care.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@sv443-network/userutils": minor
---

Added parameter to switch `debounce()` to trigger on the rising edge, instead of just the falling edge [(see docs)](https://github.com/Sv443-Network/UserUtils#debounce)
Binary file added .github/assets/debounce.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion README-summary.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ or view the documentation of previous major releases:
- [DataStore](https://github.com/Sv443-Network/UserUtils#DataStore) - class that manages a sync & async persistent JSON database, including data migration
- [autoPlural()](https://github.com/Sv443-Network/UserUtils#autoplural) - automatically pluralize a string
- [pauseFor()](https://github.com/Sv443-Network/UserUtils#pausefor) - pause the execution of a function for a given amount of time
- [debounce()](https://github.com/Sv443-Network/UserUtils#debounce) - call a function only once, after a given amount of time
- [debounce()](https://github.com/Sv443-Network/UserUtils#debounce) - call a function only once in a series of calls, after or before a given timeout
- [fetchAdvanced()](https://github.com/Sv443-Network/UserUtils#fetchadvanced) - wrapper around the fetch API with a timeout option
- [insertValues()](https://github.com/Sv443-Network/UserUtils#insertvalues) - insert values into a string at specified placeholders
- [compress()](https://github.com/Sv443-Network/UserUtils#compress) - compress a string with Gzip or Deflate
Expand Down
33 changes: 27 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ View the documentation of previous major releases:
- [DataStore](#datastore) - class that manages a sync & async persistent JSON database, including data migration
- [autoPlural()](#autoplural) - automatically pluralize a string
- [pauseFor()](#pausefor) - pause the execution of a function for a given amount of time
- [debounce()](#debounce) - call a function only once, after a given amount of time
- [debounce()](#debounce) - call a function only once in a series of calls, after or before a given timeout
- [fetchAdvanced()](#fetchadvanced) - wrapper around the fetch API with a timeout option
- [insertValues()](#insertvalues) - insert values into a string at specified placeholders
- [compress()](#compress) - compress a string with Gzip or Deflate
Expand Down Expand Up @@ -1180,22 +1180,43 @@ async function run() {
### debounce()
Usage:
```ts
debounce(func: Function, timeout?: number): Function
debounce(func: Function, timeout?: number, edge?: "falling" | "rising"): Function
```

Debounces a function, meaning that it will only be called once after a given amount of time.
This is very useful for functions that are called repeatedly, like event listeners, to remove extraneous calls.
All passed properties will be passed down to the debounced function.
The timeout will default to 300ms if left undefined.
Returns a debounced wrapper function, meaning that the given `func` will only be called once after or before a given amount of time.
This is very useful for functions that are called repeatedly, like event listeners, to remove a substantial amount of unnecessary calls.
All parameters passed to the returned function will be passed along to the input `func`

The `timeout` will default to 300ms if left undefined.

The `edge` ("falling" by default) determines if the function should be called after the timeout has passed or before it.
In simpler terms, this results in "falling" edge functions being called once at the very end of a sequence of calls, and "rising" edge functions being called once at the beginning and possibly multiple times following that, but at the very least they're spaced apart by what's passed in `timeout`.

This diagram can hopefully help bring the difference across:
<details><summary><b>Click to view the diagram</b></summary>

![debounce function edge diagram](./.github/assets/debounce.png)

</details>

<br>

<details><summary><b>Example - click to view</b></summary>

```ts
import { debounce } from "@sv443-network/userutils";

// uses "falling" edge by default:
window.addEventListener("resize", debounce((event) => {
console.log("Window was resized:", event);
}, 500)); // 500ms timeout

// using "rising" edge:
const myFunc = debounce((event) => {
console.log("Body was scrolled:", event);
}, 100, "rising"); // 100ms timeout

document.body.addEventListener("scroll", myFunc);
```

</details>
Expand Down
20 changes: 16 additions & 4 deletions lib/misc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,24 @@ export function pauseFor(time: number) {
/**
* Calls the passed {@linkcode func} after the specified {@linkcode timeout} in ms (defaults to 300).
* Any subsequent calls to this function will reset the timer and discard all previous calls.
* @param func The function to call after the timeout
* @param timeout The time in ms to wait before calling the function
* @param edge Whether to call the function at the very first call ("rising" edge) or the very last call ("falling" edge, default)
*/
export function debounce<TFunc extends (...args: TArgs[]) => void, TArgs = any>(func: TFunc, timeout = 300) { // eslint-disable-line @typescript-eslint/no-explicit-any
let timer: number | undefined;
export function debounce<TFunc extends (...args: TArgs[]) => void, TArgs = any>(func: TFunc, timeout = 300, edge: "rising" | "falling" = "falling") { // eslint-disable-line @typescript-eslint/no-explicit-any
let timer: NodeJS.Timeout | undefined;

return function(...args: TArgs[]) {
clearTimeout(timer);
timer = setTimeout(() => func.apply(this, args), timeout) as unknown as number;
if(edge === "rising") {
if(!timer) {
func.apply(this, args);
timer = setTimeout(() => timer = undefined, timeout);
}
}
else {
clearTimeout(timer);
timer = setTimeout(() => func.apply(this, args), timeout);
}
};
}

Expand Down

0 comments on commit a11ed77

Please sign in to comment.