Chalarangelo/30-seconds-of-code

Regarding the throttle function's question.

HiChen404 opened this issue ยท 7 comments

https:www.30secondsofcode.org/js/s/throttle-function/

const throttle = (fn, wait) => {
  let inThrottle, lastFn, lastTime;
  return function() {
    const context = this,
      args = arguments;
    if (!inThrottle) {
      fn.apply(context, args);
      lastTime = Date.now();
      inThrottle = true;
    } else {
      clearTimeout(lastFn);
      lastFn = setTimeout(function() {
// Can I ignore the following line of code?
//        if (Date.now() - lastTime >= wait) {
          fn.apply(context, args);
          lastTime = Date.now();
//        }
      }, Math.max(wait - (Date.now() - lastTime), 0));
    }
  };
};

I don't understand why the same condition needs to be checked inside setTimeout.

Can it be omitted?

Yes, it can be omitted. I assume your path of thought went along the lines of the Math.max() function which was used as such:
lastFn = setTimeout(delayedFunction, Math.max(wait - (Date.now() - lastTime), 0));

Setting a delay that imitates the time left for the throttle throws away the need for the conditional statement below:
if (Date.now() - lastTime >= wait) { fn.apply(context, args); lastTime = Date.now(); }

VoxyJr commented

Can it be omitted? Yes.
Should it be omitted? I'd advise not to, it's needs to help ensure intervals are followed properly.

https:www.30secondsofcode.org/js/s/throttle-function/

const throttle = (fn, wait) => {
  let inThrottle, lastFn, lastTime;
  return function() {
    const context = this,
      args = arguments;
    if (!inThrottle) {
      fn.apply(context, args);
      lastTime = Date.now();
      inThrottle = true;
    } else {
      clearTimeout(lastFn);
      lastFn = setTimeout(function() {
// Can I ignore the following line of code?
//        if (Date.now() - lastTime >= wait) {
          fn.apply(context, args);
          lastTime = Date.now();
//        }
      }, Math.max(wait - (Date.now() - lastTime), 0));
    }
  };
};

I don't understand why the same condition needs to be checked inside setTimeout.

Can it be omitted?

`const throttle = (fn, wait) => {
let inThrottle = false;

return function() {
if (!inThrottle) {
fn.apply(this, arguments);
inThrottle = true;
setTimeout(() => {
inThrottle = false;
}, wait);
}
};
};
`

In this version, after the fn is executed, inThrottle is immediately set to true, and a timeout is started to set it back to false after the specified wait period. This ensures that there's a gap between subsequent function calls and the throttling mechanism is maintained.

Hi there! HiChen404

if (Date.now() - lastTime >= wait) {
  fn.apply(context, args);
  lastTime = Date.now();
}

This condition checks whether enough time has passed since the last execution of the throttled function. If the time elapsed is greater than or equal to the specified wait time, the function is executed again.

The purpose of this condition is to handle cases where there's a delay between the setTimeout being triggered and the actual execution of the function. If the delay is longer than the specified wait time, the function should be executed immediately without waiting for the remaining time.

The purpose of the condition outside the setTimeout block is to handle cases where there is no delay and the function can be executed immediately. The purpose of the condition inside the setTimeout block is to ensure that the function is executed immediately if the delay has been too long.

In some cases, it may be safe to omit the condition. For example, if the function is not time-sensitive and it does not matter if it is executed multiple times. However, in other cases, omitting the condition could lead to unexpected results. For example, if the function is used to throttle the execution of another function, omitting the condition could lead to the other function being executed more frequently than intended.

The condition inside the setTimeout block in your throttle function is used to ensure that the throttled function (fn) is executed one last time after the specified wait time has elapsed since the last invocation. It prevents the function from waiting indefinitely before executing if there's a long delay between calls.

Here's why the condition inside setTimeout is necessary:

  1. The throttle function is designed to limit the frequency of invoking the fn function. It allows the fn function to execute only once in a specified time interval (wait milliseconds).

  2. When the throttle function is first called (i.e., the first time the throttled function is invoked), it immediately invokes fn and records the current time in lastTime. It also sets inThrottle to true, indicating that the function is currently in a throttled state.

  3. For subsequent invocations within the throttling period (wait milliseconds), the setTimeout block comes into play. If inThrottle is true, it means that a previous invocation occurred recently. In this case, the throttled function (fn) is not invoked immediately. Instead, a setTimeout is set to invoke fn after a delay equal to the remaining time left in the throttling period (wait - (Date.now() - lastTime)).

  4. Now, consider the situation where the condition inside setTimeout is omitted, and the fn function is invoked unconditionally after the setTimeout delay. This would result in fn being called repeatedly within the throttling period, ignoring the intended throttling behavior.

  5. By including the condition if (Date.now() - lastTime >= wait), the fn function is called within setTimeout only if the remaining time in the throttling period is greater than or equal to wait. This ensures that fn is invoked one last time after the specified throttling interval has passed, even if there were multiple rapid invocations of the throttled function.

It can be definitely omitted but its suggested, you should not omit the condition inside setTimeout if you want to maintain the intended throttling behavior, which ensures that the function is called at least once after the throttling period has elapsed, regardless of how frequently it was invoked within that period.

Thanks everyone for your patience. I can see there are already a couple of great answers here and the discussion is pretty useful for future readers, so I'll move this under discussions and mark the accepted answer. Cheers! ๐Ÿป