-
-
Save beaucharman/e46b8e4d03ef30480d7f4db5a78498ca to your computer and use it in GitHub Desktop.
function throttle(callback, wait, immediate = false) { | |
let timeout = null | |
let initialCall = true | |
return function() { | |
const callNow = immediate && initialCall | |
const next = () => { | |
callback.apply(this, arguments) | |
timeout = null | |
} | |
if (callNow) { | |
initialCall = false | |
next() | |
} | |
if (!timeout) { | |
timeout = setTimeout(next, wait) | |
} | |
} | |
} | |
/** | |
* Normal event | |
* event | | | | |
* time ---------------- | |
* callback | | | | |
* | |
* Call search at most once per 300ms while keydown | |
* keydown | | | | | |
* time ----------------- | |
* search | | | |
* |300| |300| | |
*/ | |
const input = document.getElementById('id') | |
const handleKeydown = throttle((arg, event) => { | |
console.log(`${event.type} for ${arg} has the value of: ${event.target.value}`) | |
}, 300) | |
input.addEventListener('keydown', (event) => { | |
handleKeydown('input', event) | |
}) |
As you just proved... There are varying opinions. 😜
To throttle a function means to ensure that the function is called at most once in a specified time period
Not necessarily once. It could be at most X times in a specified time period. So the solution function which throttles a given function should receive 3 params
- the number of times to call (in your case, that is 1)
- the specified period of time
- the callback function itself.
Use RxJs and you'll be happy 😀
Typescript version of throttle function with final and immediate invocations
// Throttle with ensured final and immediate invocations
const throttle = <T extends []> (callback: (..._: T) => void, wait: number): (..._: T) => void => {
let queuedToRun: NodeJS.Timeout | undefined;
let previouslyRun: number;
return function invokeFn(...args: T) {
const now = Date.now();
queuedToRun = clearTimeout(queuedToRun) as undefined;
if (!previouslyRun || (now - previouslyRun >= wait)) {
callback(...args);
previouslyRun = now;
} else {
queuedToRun = setTimeout(invokeFn.bind(null, ...args), wait - (now - previouslyRun));
}
};
};
Thanks to @robertmirro and @FRSgit
Nice one @undergroundwires! But I've skipped leading invocation on purpose, because I was aiming for the simplest implementation. Also, I found out that having this immediate leading method firing is not always a good idea, but that of course depends on a use case.
If we want to write the "fullest" throttle fn I think there should be a possibility to opt out from leading & trailing callback calls. Exactly as they do in lodash.
Here's my version, using TypeScript. It's returning a callback to cancel the timeout, useful if use have to use some cleanup function.
const throttle = <TArgs extends unknown[] = []>(
callback: (...args: TArgs) => void
): ((ms: number, ...args: TArgs) => (() => void)) => {
let timeout: NodeJS.Timeout | undefined;
let lastArgs: TArgs;
const cancel = () => {
clearTimeout(timeout);
};
return (ms, ...args) => {
lastArgs = args;
if (!timeout) {
timeout = setTimeout(() => {
callback(...lastArgs);
timeout = undefined;
}, ms);
}
return cancel;
};
};
Usage:
const cb = (n) => console.log(n);
const throttledCb = throttle(cb);
throttledCb(100, 1);
// wait 50ms
throttledCb(100, 2);
// wait 50ms
// prints 2
To throttle a function means to ensure that the function is called at most once in a specified time period (for instance, once every 5 seconds).