Simple Throttle in JavaScript

Simple throttle in JavaScript

I would use the underscore.js or lodash source code to find a well tested version of this function.

Here is the slightly modified version of the underscore code to remove all references to underscore.js itself:

// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time. Normally, the throttled function will run
// as much as it can, without ever going more than once per `wait` duration;
// but if you'd like to disable the execution on the leading edge, pass
// `{leading: false}`. To disable execution on the trailing edge, ditto.
function throttle(func, wait, options) {
var context, args, result;
var timeout = null;
var previous = 0;
if (!options) options = {};
var later = function() {
previous = options.leading === false ? 0 : Date.now();
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};
return function() {
var now = Date.now();
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func.apply(context, args);
if (!timeout) context = args = null;
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
};
};

Please note that this code can be simplified if you don't need all the options that underscore support.

Please find below a very simple and non-configurable version of this function:

function throttle (callback, limit) {
var waiting = false; // Initially, we're not waiting
return function () { // We return a throttled function
if (!waiting) { // If we're not waiting
callback.apply(this, arguments); // Execute users function
waiting = true; // Prevent future invocations
setTimeout(function () { // After a period of time
waiting = false; // And allow future invocations
}, limit);
}
}
}

Edit 1: Removed another reference to underscore, thx to @Zettam 's comment

Edit 2: Added suggestion about lodash and possible code simplification, thx to @lolzery @wowzery 's comment

Edit 3: Due to popular requests, I added a very simple, non-configurable version of the function, adapted from @vsync 's comment

Javascript Function Throttling

In order for throttle(func, limit) to work, there can only be one instance of its product.

The problem is that the onClick function in your example creates a new instance each time it is called.

This makes the underlying inThrottle variable meaningless, as a new copy is created for each click.

The solution is to call one single instance the product of throttle(foo, 50000) directly.

Also, foo itself should be passed (not its product).

See below for a practical example, as well as closures and scope for more info.

// Foo.
const foo = (...args) => {
$("#respond").append("|");
}

// Throttle.
const throttle = (func, limit) => {
let inThrottle
return (...args) => {
if (!inThrottle) {
func(...args)
inThrottle = setTimeout(() => inThrottle = false, limit)
}
}
}

// On Click.
const onClick = throttle(foo, 1000)

// Button - Click.
$('#button').click(onClick);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="button" id="button" value="Click Me" />
<div id="respond"></div>

JavaScript Throttle always returning function

Throttling works differently.

First, you should name your throttle function just throttle, as it is not specifically linked with display. Then, you should call this throttle to get a function that is specific to display. And only then you would use that secondary function in the actual use case you have for it, i.e. with the timer.

Here is your code corrected:

const throttle = (func, limit) => {
let flag = true;
return function() {
if(flag) {
func.apply(this, arguments);
flag = false;
setTimeout(() => flag = true, limit);
}
}
};

const throttleDisplay = throttle(() => console.log("Hi"), 6000);

for(let i=1; i<=10; i++) {
setTimeout(throttleDisplay, i*1000);
}

I can't get a simple throttle function to work

You need to build a closure, that means the variable flag must preserve its value between each logKey() invocation. The solution is to store it global (as below) or in a parent scope where logKey can access it.

window.addEventListener("keydown", logKey);
var flag = true;var limit = 10000;
function logKey(e) {
document.getElementById("key").innerHTML = flag; if (flag) { if (e.which == 87) { flag = false; document.getElementById("demo").innerHTML = "forwards"; setTimeout(() => { flag = true; }, limit); } else if (e.which == 83) { document.getElementById("demo").innerHTML = "backwards"; flag = false; setTimeout(() => { flag = true; }, limit); } } else { document.getElementById("demo").innerHTML = "empty"; }}
<div id="demo"></div><div id="key"></div>

How to use one throttle for multiple functions?

I think the best way is to just create your own throttle function. You cant access lodash's timeout so its impossible to have full control on shared timers.

most common throttle approaches store timers/setTimeout() in a variable to track whether a function should be cancelled or not, so as long as you can access that variable you can have shared timers between different functions, even with different delays, something like this:

export const createTimeout = () => {
let timeout: any = null;

const _start = (delay: number) => {
timeout = setTimeout(() => {
timeout = null
}, delay);
};

const _persists = () => (timeout !== null);

return {
persists: _persists,
start: _start,
};
};

with this code, you can use it like this


const myTimeout = createTimeout()

function nextPage() {
if (myTimeout.persists()) return
// logic here

myTimeout.start(2000) // start/refresh timeout
}

function prevPage() {
if (myTimeout.persists()) return
// logic here

myTimeout.start(3000)

}

In this code, whenever I start the timeout, it stores an id from the return value of setTimeout() to variable timeout, then the callback inside the setTimeout() must reassign the value of variable timeout in order to determine if the timer is already finished.

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

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'))

Throttle function for 2 seconds

Just prevent the repeated function call after a function is already called. You can use a simple flag to check if the html should be updated or a processInfo update is already started.

And use setTimeout(function, milliseconds) instead of setIntervall(function, milliseconds) to execute the update of processInfo function only ones.

Throttle in JS: Why in console I'm seeing only 1st and last output?

It throttles all the request that come up in the specified interval after invoking the first one (which prints 1) and Subsequent calls only return the result of the last invocation (which prints 5) after the interval is over. So, If you just do something like this, you'll see each of the values getting printed

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

let f1000 = _.throttle(f, 1000);

setTimeout(()=>f1000(1), 1000)
setTimeout(()=>f1000(2), 2000)
setTimeout(()=>f1000(3), 3000)
setTimeout(()=>f1000(4), 4000)
setTimeout(()=>f1000(5), 5000)

Or, if you just reduce the throttle time to 0 milliseconds, it will print all the values.

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

let f1000 = _.throttle(f, 0); //reduce throttle to 0

f1000(1);
f1000(2);
f1000(3);
f1000(4);
f1000(5);

I got this clue when I put a debugger in the function and kept the dev tool open. In that case it was printing each of the numbers (since, there was sufficient delay against each invocation). You can see this for yourself too.

const f = function (a) {
debugger;
console.log(a);
};

Also, you might like to read the doc: https://lodash.com/docs/4.17.15#throttle

_.throttle executing API more than once

Every time you scroll, you fire dispatch, which in turn re-runs the component, adding another event listener. This will continue infinitely.

You'll only want the event listener to be added the first time the component is rendered. You can do this with the useEffect hook.

useEffect(() => {
window.addEventListener('scroll', () => throttledReq(loaded, TIMEOUT, dispatch));
}, []);

The empty array at the end means that it doesn't have any dependencies, so it will only run once whenever the component is rendered. Every rerender won't call the callback.

Your loaded value should use a useEffect as well, but in this case with the loaded variable as a dependency. Every time loaded is changed (and on the first render), the callback is fired and the logic assessed.

useEffect(() => {
if (loaded === 0) {
API.Post.getAllPosts(loaded)(dispatch);
}

if (loaded % 5 === 0) {
window.scrollTo(0, document.body.scrollHeight * 0.6)
}
), [loaded]);

Since you're trying to call a certain logic whenever a scroll position is reached, or some element is in view, I recommend you take a look at the Intersection Observer API which eliminates the requirement of a throttled onscroll function.



Related Topics



Leave a reply



Submit