javascript-tutorial / en.javascript.info

Modern JavaScript Tutorial
https://javascript.info
Other
23.13k stars 3.84k forks source link

Solution for throttle decorator is incorrect. (Decorators and forwarding, call/apply) #3613

Open void5253 opened 8 months ago

void5253 commented 8 months ago

Original code: https://javascript.info/call-apply-decorators#throttle-decorator

Here's a small code snippet to show where it doesn't work.

function f(a) { console.log(a) };

let g = throttle(f, 1000);

for(let i = 0; i < 1e8; i++) g(i);

Expected Output

1, 249204, 452039, ... , 9999999 (These are random increasing numbers)

Output

1, 9999999

Why does it fail?

function wrapper() {

    if (isThrottled) { // (2)
      savedArgs = arguments;
      savedThis = this;
      return;
    }
    isThrottled = true;

    func.apply(this, arguments); // (1)

    setTimeout(function() {
      isThrottled = false; // (3)
      if (savedArgs) {
        wrapper.apply(savedThis, savedArgs);
        savedArgs = savedThis = null;
      }
    }, ms);
  }

In above, isThrottled = false assignment is done inside setTimeout callback. However, only one callback is pushed into task queue and it isn't executed until stack is empty (for loop has to be completed). isThrottled is always true => setTimeout isn't called => one callback (that was registered for initial false isThrottled) => cb executed at end and outputs last value => output: 1, 9999999.

Correct Solution: https://github.com/javascript-tutorial/en.javascript.info/pull/2844

This PR giving an alternative solution.