Javascript Function Throttling - javascript

I want to use a JS Throttle. But i'm struggeling to get it work correctly.
I tried the code from this article:
https://codeburst.io/throttling-and-debouncing-in-javascript-b01cad5c8edf
But the Throttle does not work as intended, since everytime i click on the button, one "|" is added to the div. No clicks were discarded.
where is the misstake?
function foo() {
$("#respond").append("|");
}
const throttle = (func, limit) => {
let inThrottle
return function() {
const args = arguments
const context = this
if (!inThrottle) {
func.apply(context, args)
inThrottle = true
setTimeout(() => inThrottle = false, limit)
}
}
}
var onClick = function() {
throttle(foo(), 50000);
};
$('#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>

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>

I know the question is old.
I made a small snippet. I think it should qualify as a simple way to throttle a function. This will guarantee a call every wait milliseconds.
function throttle( fn, wait ){
let lastCall = 0;
return function(){
if( Date.now() - lastCall > wait ){
lastCall = Date.now()
fn()
}
}
}
var counter = 0
var count = () => document.querySelector("#count").textContent = ++counter
window.addEventListener('scroll', throttle( count, 1000 ) )
body{
height: 2000px;
}
<h1 id='count'>
0
</h1>

Your onClick is creating a new throttled function on every invoke. You have to ensure that is only throttled once
var onClick = function() {
throttle(foo(), 50000);
};
// TO
var onClick = throttle(foo, 50000);

Related

how to trigger a function after a delay only once with the latest value of the textbox

I have react app & need to trigger a function once user stops typing in the textbox. This is what I tried.
const inputHandler =(e:SynthenticEvent) => {
let timer: any = null;
clearTimeout(timer);
timer = setTimeout(() => {
console.log('hi');
//in actual sending an Http request, however hiding it here for brevity
},1000);
}
<input type="text" onChange={inputHandler} name="userInput" />
What's happening as of now, that the inputHandler trigger after 1000ms but it trigger the multiples times (ex 100 is triggering 3 times, 1000 triggers 4 times)
Which is not ideal or expected?
I thought to make use of useEffect, but on doing so inputHandler() is not in identified/scope by textbox?
What I'm looking is at trigger the function after 1000ms but once with
latest value of the texbox?
Thanks!
This called debounce
Debounce function.
Javascript code.
function debounce(func, timeout = 300){
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => { func.apply(this, args); }, timeout);
}
}
Typescript code.
function debounce<Params extends any[]>(func: Function, timeout =
300) {
let timer: ReturnType<typeof setTimeout>;
return (...args: Params) => {
clearTimeout(timer);
timer = setTimeout(() => { func.apply(this as Function,
args); }, timeout);
}
}
To use it.
const handleChange = debounce(() => console.log('hi'))
<input type="text" onChange={inputHandler} name="userInput" />
Thanks to #Mina for letting me know about debouncing. On googling around debounce in javascript, I found a minor fix in my code which addressed my requirement precisely
I was almost close, all I was doing wrong was that I had defined & initialized the timer inside the function, just moving outside the target function resolved the issue
let timer: any = null; //moved outside the function
const inputHandler =(e:SynthenticEvent) => {
clearTimeout(timer);
timer = setTimeout(() => {
console.log('hi');
//in actual sending an Http request, however hiding it here for brevity
},1000);
}

debounce function not working in javascript

I'm having problem understanding why the below debounce code does not work?
you can see the below code in the following: link
`
HTML:
<input type="text" onkeyup="betterFunction(event)"/>
JS:
let newValue;
let counter = 0;
const getData = () => {
// dummy call to API and get Data
console.log("Fetching Data ..", newValue,counter++);
}
const debounce = function (fn, d) {
let timer;
return function () {
clearTimeout(timer);
timer = setTimeout(() => {
fn();
}, d);
}
}
const betterFunction = ({target:{value}}) => {
newValue=value;
debounce(getData, 2000); // **line 1**. placing this line of code debouncing does not happen
intermediate() // **line 2**. replacing this line of code with the above line debouncing works
}
const intermediate = debounce(getData, 2000);
`
I understand that the debounce function returns another function which acts like a closure in JavaScript but why the above line 1 code does not work but the line 2 code works
debounce function returns a function which is never called when you call debounce as
debounce(getData, 2000);
dobounce function doesn't needs to return a function. You just need following steps to implement debounce function:
Check if timer is undefined or not. If not, that means there's a timeout that we need to cancel.
After that set a new timer by calling setTimeout() that calls the given function after specific amount of time.
Also, timer should not be a local variable because you don't want it to reset whenever debounce function is called.
let counter = 0;
let newValue;
let timer;
const getData = () => {
console.log("Fetching Data ..", newValue, counter++);
}
const debounce = function(fn, d) {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(fn, d);
}
const betterFunction = (e) => {
newValue = e.target.value;
debounce(getData, 2000);
}
<input type="text" onkeyup="betterFunction(event)" />
If you don't want to declare timer as a global variable and want to return a function from debounce function, then you need to call the debounce function once initially and whenever keyup event fires on the input element, you call the function returned from the debounce function instead of calling the debounce function.
let counter = 0;
let newValue;
const getData = () => {
console.log('Fetching Data ..', newValue, counter++);
};
const debounce = function(fn, d) {
let timer;
return function() {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(fn, d);
};
};
const intermediate = debounce(getData, 2000);
const betterFunction = (e) => {
newValue = e.target.value;
intermediate();
};
<input type="text" onkeyup="betterFunction(event)" />
i hope that what you want :
let counter = 0;
// you need to define timer and newValue here first
let timer , newValue;
// defining input as varible for good usage better than usage in html
var input = document.querySelector("#inp");
const getData = () => {
// increment first better than in console :)
counter+=1;
console.log("Fetching Data .." , newValue , counter);
// as last step clear timer for next timeout
clearTimeout(timer);
}
// givin value direct to timer directlly worked better than return
const debounce = function (fn, d) {
timer = setTimeout(fn, d);
}
const betterFunction = () => {
// newvalue must equal input value
newValue = input.value;
// and then calling debounce as last step
debounce(getData, 2000);
}
// here giving onkeyup event to input for getting values and start working :)
input.onkeyup = betterFunction;

Debouncing a function not working - REACT functional component

I am new to React so as part of learning I am trying to do a js debounce function when typed in input box (a search box simulation). But for some reason it is not working. The function is getting called each for key up than once for 2000 ms or delay specified in timeout. Below is the link to sandbox code for your reference. I have referred across blogs, the implementations seems the same yet I could not figure out what is the issue.
https://codesandbox.io/s/react-debouncing-9g7tc?file=/src/search.component.js
Issue :
const debounce = (func, delay) => {
let t; // <-- this will be new variable each time function get called
return function() {
clearTimeout(t); // <--- this also points to the new one, so it will clear nothing
// so all the previously called func will be called
t = setTimeout(() => func(), delay);
};
};
1st Solution : useRef
const t = useRef(null);
const debounce = (func, delay) => {
return function() {
clearTimeout(t.current); // <--- pointing to prev setTimeout or null
t.current = setTimeout(() => func(), delay);
};
};
WORKING DEMO
2nd Solution : Define t outside scope of SearchUi
var t;
const SearchUi = () => {
...
const debounce = (func, delay) => {
return function() {
clearTimeout(t);
t = setTimeout(() => func(), delay);
};
};
...
};
WORKING DEMO

Custom debounce function not working with anonymous functions

I'm trying to create a custom debounce function:
const debounced = [];
const cancelFunc = timeout => () => {
clearTimeout(timeout);
};
function debounce(fn, wait, ...args) {
let d = debounced.find(({ func }) => func === fn);
if (d) {
d.cancel();
} else {
d = {};
debounced.push(d);
}
d.func = fn;
d.timeout = setTimeout(fn, wait, ...args);
d.cancel = cancelFunc(d.timeout);
}
If I use with a named function, it works as intended:
debounce(foo, 1000); // called once with 5 clicks in 1 second
But I can't get it to work with anonymous functions:
debounce(() => { foo(5); }, 1000); // called 5 times with 5 clicks in 1 second
I created a pen here: https://codepen.io/anon/pen/gQvMdR?editors=1011
This happens because of your find condition. Let's back up, and consider this bit of code:
if (
(function(){ return 1 }) === (function(){ return 1 })
) {
console.log('The functions are equal');
} else {
console.log('The functions are NOT equal');
}
// logs 'The functions are NOT equal'
Even though I wrote two identical anonymous functions, they are not strictly equal to each other. When you pass in that anonymous function, that is essentially what you are doing. So, when you search for your array for a previously found function, it will never find a match, because each time debounce(() => { foo(5); }, 1000); is called it creates a new function. Since it'll never find a match, it will never be canceled.
As mentioned by #SLaks "Each call creates a separate function, so you won't find it in the array."
So you just need to store something in the array to match it to, you can use .toString()
// ================
const debounced = [];
const cancelFunc = timeout => () => {
clearTimeout(timeout);
};
function debounce(fn, wait, ...args) {
let d = debounced.find(({ funcString }) => funcString === fn.toString());
if (d) {
d.cancel();
} else {
d = {};
debounced.push(d);
}
d.func = fn;
d.funcString = fn.toString()
d.timeout = setTimeout(fn, wait, ...args);
d.cancel = cancelFunc(d.timeout);
}
// ================
function foo(value) {
console.log('value:', value)
}
function onClickBroken() {
debounce(() => { foo(5); }, 1000);
}
<button onClick="onClickBroken()">Click me 5 times</button>

How to Prevent Timeout Function from Running Twice

I am trying to automatically pass data from an input field into a function if no new data has been entered after 1000 ms. However, its behavior is inconsistent and sometimes the function runs twice on a single input.
<paper-input id="itemId" on-input="automaticInput"></paper-input>
...
automaticInput() {
let timeout = null;
let that = this;
console.log(timeout); // logging each keystroke
input();
function input() {
console.log('input');
clearTimeout(timeout);
timeout = setTimeout(function() {
that.validateInput();
}, 1000);
}
}
validateInput() {
if (this.$.itemId.value) {
this.doSomething(); // runs twice
}
}
How do can I properly set the automaticInput function to only run once per input string?
What you need is a debounce function. Your code is mostly here, just move timeout var out of automaticInput:
let timeout = null;
automaticInput() {
clearTimeout(timeout);
let that = this;
timeout = setTimeout(function() {
that.validateInput();
}, 1000);
}
Or better create debounce fn like:
function debounce(fn, timeout = 0) {
let timeoutId = null;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn(...args), timeout);
}
}
and then you code become:
const debouncedValidateInput = debounce(this.validateInput.bind(this), 1000);
<paper-input id="itemId" on-input="debouncedValidateInput"></paper-input>
If you use something like lodash it already shipped with such helper.

Categories

Resources