-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: adding basic throttle function
- Loading branch information
1 parent
4c386b7
commit 9292462
Showing
1 changed file
with
156 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
# 4. implement basic throttle() | ||
|
||
What is Throttling in JavaScript? | ||
Throttling implies limiting the number of times a function gets called in a certain time period. It will prohibit a function from executing if we have invoked it “recently.” Throttling also guarantees that a function runs at a consistent rate. Throttling or sometimes also called throttle function, is a practice used in websites. | ||
|
||
To better understand throttling, consider the following example. Assume you’re heading to an ATM to get some cash. The bank’s guidelines state that you may only withdraw cash once every hour. You proceeded to withdraw cash from the ATM. You afterward discovered that you require additional funds. When you attempted to withdraw more cash, the ATM rejected your request, stating that you could only do so after an hour. No matter how many times you attempt, the ATM will deny your request until one hour has passed since your last transaction. It is known as throttling. We are rate-limiting a function execution over a period of time, in this case, one hour, for performing an ATM transaction. | ||
|
||
|
||
### Problem | ||
|
||
https://bigfrontend.dev/problem/implement-basic-throttle | ||
|
||
# | ||
|
||
### Problem Description | ||
|
||
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** | ||
|
||
1. please follow above spec. the behavior is not exactly the same as `lodash.throttle()` | ||
|
||
2. because `window.setTimeout` and `window.clearTimeout` are 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. | ||
|
||
```js | ||
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']); | ||
``` | ||
|
||
# | ||
|
||
### Solution | ||
|
||
```js | ||
/** | ||
* @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); | ||
}; | ||
} | ||
``` | ||
|
||
|
||
# | ||
|
||
### Usage | ||
|
||
```js | ||
function logHi() { | ||
console.log("Hi"); | ||
} | ||
|
||
const throttledLogHi = throttle(logHi, 500); | ||
|
||
throttledLogHi(); // Hi | ||
throttledLogHi(); // (nothing) | ||
throttledLogHi(); // (nothing) | ||
throttledLogHi(); // (nothing) | ||
``` | ||
|
||
|
||
# | ||
|
||
### Explanation | ||
|
||
The `throttle` function is designed to limit the number of times a function can be executed over a certain period. In this case, the `wait` time is set to 500 milliseconds. | ||
|
||
Here's a step-by-step explanation of what happens when you call `throttledLogHi` four times in quick succession: | ||
|
||
1. Call `throttledLogHi` the first time: `isWaiting` is `false`, so `logHi` is executed immediately and "Hi" is logged to the console. Then `isWaiting` is set to `true`, which prevents `logHi` from being executed again until `isWaiting` is set back to `false` after the `wait` time has passed. | ||
|
||
2. Call `throttledLogHi` the second, third, and fourth time: These calls happen while `isWaiting` is still `true`, so `logHi` is not executed and nothing is logged to the console. The `lastCallArgs` are updated with the arguments of the last call, but since `logHi` doesn't take any arguments, this doesn't have any effect. | ||
|
||
3. After the `wait` time has passed, `isWaiting` is set back to `false`. If there were any calls to `throttledLogHi()` during the `wait` time (which there were), `logHi` is executed again with the arguments of the last call (which were `null`), and "Hi" is logged to the console again. | ||
|
||
This is why you see "Hi" logged to the console twice, even though `throttledLogHi()` was called four times. The throttling mechanism ensures that `logHi` is not called more than once every 500 milliseconds, regardless of how many times `throttledLogHi` is called. | ||
|
||
|
||
# | ||
|
||
### Reference | ||
|
||
- [Simple throttle in js](https://stackoverflow.com/questions/27078285/simple-throttle-in-js#) | ||
- [Write a better throttle function with Underscore](https://gist.github.com/pinglu85/fbe672cb84faa987a1e97e20d844b108) |