JS Throttle Explained

  1. Init:

    1. var shouldWait defaults to false

    2. var waitingArgs defaults to null

  2. return a function that

    1. if shouldWait, set waitingArgs to current invocation's args, then return

    2. if not shouldWait:

      1. run the callback immediately

      2. set shouldWait to true

      3. set a timer with delay to do the following things (described in timeoutCallback):

        1. if there aren't waitingArgs, just flip shouldWait to false

        2. otherwise, run the callback immediately; clear waitingArgs; then set a timer to recursively invoke self

function throttle(callback, delay) {
    let shouldWait = false
    let waitingArgs = null

    const timeoutCallback = () => {
        if (waitingArgs === null) {
            shouldWait = false
            return
        }

        callback.apply(this, waitingArgs)
        waitingArgs = null
        setTimeout(timeoutCallback, delay)
    }

    return (...args) => {
        if (shouldWait) {
            waitingArgs = args
            return
        }

        callback.apply(this, args)
        shouldWait = true
        setTimeout(timeoutCallback, delay)
    }
}