Skip to content

Commit

Permalink
feat: adding basic throttle function
Browse files Browse the repository at this point in the history
  • Loading branch information
Shaban-Eissa committed May 27, 2024
1 parent 4c386b7 commit 9292462
Showing 1 changed file with 156 additions and 0 deletions.
156 changes: 156 additions & 0 deletions 4.implement-basic-throttle.md
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)

0 comments on commit 9292462

Please sign in to comment.