Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I throttle a function without dropping function calls in JS?

I need a function to have certain amount of time between one function call and another, very similar to what a throttle would do, but I need all the function calls to be attended.

Let me explain:

Let's imagine we have a throttle of 10 seconds on a function foo, and an event that calls foobar. The event is fired 3 times, one every 5 seconds. Only the first one and the last one would be attended while the one in the middle would be ignored.

Normal throttle:

[event]--5s--[event]--5s--[event]
[foo]---------10s---------[foo]

What I need is that the event is always listened, but 10 seconds after the previous one.

[event]--5s--[event]--5s--[event]
[foo]----------10s--------[foo]---------10s--------[foo]

Any ideas? I thought about throttle and also debouncer but none of them would fit what I need to do.

like image 839
Dani Avatar asked Aug 09 '26 05:08

Dani


2 Answers

This is pretty simple to do. Just create an array of function objects, and pull the oldest one off every 10 seconds and execute it.

    const queue = []
    
    function rateLimit(fn) {
      queue.push(fn)
    }
    
    setInterval(() => {
      const nextFn = queue.shift() // remove the first function, and return it.
      if (nextFn) nextFn() // execute the first function if it exists.
    }, 1000) // Your interval in milliseconds. Set to 1s for testing.
    
    
    rateLimit(() => console.log('A'))
    rateLimit(() => console.log('B'))
    rateLimit(() => console.log('C'))

You could add some logic to clear the interval when the queue is empty, and start it up again fresh when a new item comes in so that the first call always happens immediately if it's not waiting. But I leave that an exercise for you.

like image 97
Alex Wayne Avatar answered Aug 10 '26 18:08

Alex Wayne


Here's my take on it. This turns an existing function into a throttled version. It doesn't use a queue, and instead schedules the function calls via setTimeout if they happen too quickly.

function throttle(expensiveFunction, interval = 1000) {
    let latestExecution = null;

    return function throttled() {
        const now = performance.now();

        // Check if function has been called before
        if (latestExecution !== null) {
            // Check if next allowable function call is in the future
            const nextExecution = latestExecution + interval;

            if (nextExecution > now) {
                // Next allowed call is in the future,
                // so queue it up and advance latestExecution
                latestExecution = nextExecution;
                setTimeout(expensiveFunction, nextExecution - now);

                return;
            }
        }

        // At this point, either the function has never been called before,
        // or the next allowed function call is in the past/present.
        // So, call it right away.
        latestExecution = now;
        expensiveFunction();
    };
}

// Example usage
function timid() {
    console.log(performance.now(), "I don't want to be called too quickly...");
}

const throttled = throttle(timid, 2000);
// Will be called 3 times, but with a 2s delay inbetween
throttled();
throttled();
setTimeout(throttled, 1000);
like image 29
a cat Avatar answered Aug 10 '26 20:08

a cat



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!