1

this is probably a simple mistake. I'm trying to implement a throttle function (Credit to our man webdevsimplified for quality content: https://blog.webdevsimplified.com/2022-03/debounce-vs-throttle/).

My throttle implementation is not working and I am not sure why. How can I get the throttle working?

function printHi() {
    console.log("Hi");
}

function throttle(cb, delay = 1000) {
    let shouldWait = false
    let waitingArgs
    const timeoutFunc = () => {
        if (waitingArgs == null) {
            shouldWait = false
        } else {
            cb(...waitingArgs)
            waitingArgs = null
            setTimeout(timeoutFunc, delay)
        }
    }

    return (...args) => {
        console.log("should wait?", shouldWait);

        if (shouldWait) {
// It never goes in here...?
            waitingArgs = args
            return
        }

        cb(...args)
        shouldWait = true
        setTimeout(timeoutFunc, delay)
    }
}
<button onclick="(function () {
    throttle(printHi)();
})()">Click me</button>

Consecutive button clicks print to the console:

should wait? false
Hi
should wait? false
Hi
should wait? false
Hi

shouldWait is never printed true, even though it should...

Stacks Queue
  • 848
  • 7
  • 18
daCoda
  • 3,583
  • 5
  • 33
  • 38
  • 1
    This API is meant to be used like this: `const throttledFunction = throttle(printHi);`. Then calling `throttledFunction()` several times will produce the expected output. The function returned in `throttle` [closes over](/a/111111/4642212) `shouldWait`. If you just keep calling `throttle` itself all the time, it will just create a new closure, thus defeating the purpose of this API. – Sebastian Simon Nov 20 '22 at 06:11
  • 1
    @SebastianSimon oh yeah, this might have been a better solution than my answer. – CryptoAlgorithm Nov 20 '22 at 06:20
  • Cheers @SebastianSimon, so what I was erroneously doing was creating a 'new' version of `throttle` at each time. I needed a reference to it instead (i.e. `throttledFunction`). This closure stuff is confusing! Thanks – daCoda Nov 20 '22 at 13:02

1 Answers1

3

Your original implementation didn't work because shouldWait and waitingArgs were scoped to the function, so every function run had a fresh set of these variables with shouldWait = false.

You might have made this mistake due to the scope differences between var and let, the former was globally scoped if used this way.

Here is my solution, which simply moved the 2 variables out of the function.

function printHi() {
    console.log("Hi");
}

let shouldWait = false
let waitingArgs
function throttle(cb, delay = 1000) {
    const timeoutFunc = () => {
        if (waitingArgs == null) {
            shouldWait = false
        } else {
            cb(...waitingArgs)
            waitingArgs = null
            setTimeout(timeoutFunc, delay)
        }
    }

    return (...args) => {
        console.log("should wait?", shouldWait);

        if (shouldWait) {
// It never goes in here...?
            waitingArgs = args
            return
        }

        cb(...args)
        shouldWait = true
        setTimeout(timeoutFunc, delay)
    }
}
<body>
    <button onclick="(function () {
        throttle(printHi)();
    })()">Click me</button>
</body>
CryptoAlgorithm
  • 838
  • 5
  • 15