https://bigfrontend.dev/problem/implement-basic-throttle
Throttling is a common technique used in Web Application, in most cases using lodash solution would be a good choice.
could you implement your own version of basic throttle()?
In case you forgot, throttle(func, delay) will return a throttled function, which will invoke the func at a max frequency no matter how throttled one is called.
Here is an example.
Before throttling we have a series of calling like
─A─B─C─ ─D─ ─ ─ ─ ─ ─ E─ ─F─G
After throttling at wait time of 3 dashes
─A─ ─ ─C─ ─ ─D ─ ─ ─ ─ E─ ─ ─G
Be aware that
- call A is triggered right way because not in waiting time
- function call B is swallowed because B, C is in the cooling time from A, and C is latter.
notes
-
please follow above spec. the behavior is not exactly the same as
lodash.throttle() -
because
window.setTimeoutandwindow.clearTimeoutare not accurate in browser environment, they are replaced to other implementation when judging your code. They still have the same interface, and internally keep track of the timing for testing purpose.
Something like below will be used to do the test.
let currentTime = 0;
const run = (input) => {
currentTime = 0;
const calls = [];
const func = (arg) => {
calls.push(`${arg}@${currentTime}`);
};
const throttled = throttle(func, 3);
input.forEach((call) => {
const [arg, time] = call.split('@');
setTimeout(() => throttled(arg), time);
});
return calls;
};
expect(run(['A@0', 'B@2', 'C@3'])).toEqual(['A@0', 'C@3']);/**
* @param {Function} func
* @param {number} wait
*/
function throttle(func, wait) {
// Track if we are waiting. Initially, we are not.
let isWaiting = false;
// Track arguments of last call
let lastCallArgs = null;
return function throttled(...args) {
// If we are waiting,
if (isWaiting) {
// ...store arguments of last call
lastCallArgs = args;
return;
}
// If we are not waiting, execute 'func' with passed arguments
func.apply(this, args);
// Prevent future execution of 'func'
isWaiting = true;
// After wait time,
setTimeout(() => {
// ...allow execution of 'func'
isWaiting = false;
// If arguments of last call exists,
if (lastCallArgs) {
// ...execute function throttled and pass last call's arguments
// to it. Since now we are not waiting, 'func' will be executed
// and isWaiting will be reset to true.
throttled.apply(this, lastCallArgs);
// ...reset arguments of last call to null.
lastCallArgs = null;
}
}, wait);
});
}