Desired Outcome:
I am getting a script's status from an API and then checking what the status is.
If the status = 'XYZ', I need to call the getStatus function again (using setInterval),
else I just need to return/do nothing.
Issue:
The major issue is that clearInterval() is not working and the getStatus() function is getting called after the mentioned interval (10 seconds here) again and again, EVEN THOUGH the status IS NOT EQUAL to 'XYZ'.
Please help out. Thanks in advance.
const [intervalId, setIntervalId] = useState();
useEffect(() => {
getStatus();
}, []);
const getStatus = async () => {
await getScriptStatus()()
.then(response => {
if (response.data[0].status == 'XYZ') {
setIntervalId(
setInterval(() => {
getStatus();
}, 10000)
);
}
else
return () => clearInterval(intervalId);
})
.catch(error => {
errorMessage(error);
});
UPDATE:
Using React.useRef() instead of useState() for intervalId and clearing interval before using setInterval() in the 'if' condition worked or me.
Thank you for the helpful answers.
Use setTimeout instead of using setInterval.
For more details visit : Documentation on setTimeout
intervalId doesn't need to be a "state", rather store is as a ref and not a state.
Notice I changed your function from using .then().catch() to using async/await.
I think that the problem you're facing is caused because React State is asynchronous - the setIntervalId didn't update the intervalId yet, when you're trying to already access intervalId's new value
const intervalId = useRef(null);
const getStatus = async () => {
try {
const response = await getScriptStatus()()
if (response.data[0].status == 'XYZ') {
intervalId.current = setInterval(getStatus, 10000);
}
else return () => clearInterval(intervalId.current);
} catch (error) {
errorMessage(error);
}
}
Another suggestion:
The getStatus function should not act as a useEffect-function, because it's very confusing to see that getStatus returns a function.. and then you need to trail back to "AH it's a useEffect-function.. and the function returned is a cleanup function.. " - not good.. .
so.. My Suggested Options for that:
Either change the getState function to only getStatus.. and then when you call it from inside the useEffect- also handle the case in which you want to return a function (cleanup function)
Less suggested: Have all the content straight inside the useEffect. And since you want to use async/await -> create a self-calling-async-function: useEffect(()=>{ (async () => { await func(); })(); }, []);
Update:
In addition to all that ^
You really don't need the setInterval (like others have suggested). setTimeout will do just fine. But you might still want the clearTimeout (changed from clearInterval) if the functio might be called on different occasions
Main problem here is most probably that you are installing new intervals recursively: function installs interval that calls function that installs interval etc, effectively producing cascade of intervals. These are more or less in-revocable, because their IDs live ephemerally in function scope that installed them and are then thrown away.
You can observe it with slightly modified version of your code in this sandbox.
Change return () => clearInterval(intervalId); to this clearInterval(intervalId);.
I'm trying to understand why this code fires off immediately.
const test = setInterval(() => {
console.log("test"), 2;
});
Why does it happen? I didn't even do test();
Because you need to pass 2 as an argument to setInterval - currently it's not available. Also note that the only reason anyone assigns the result of setInterval is to call clearInterval. If you want to have it run when you want, make test a function.
const test = () => setInterval(() => console.log("test"), 2);
To avoid such syntax errors we can do like this,
const test = setInterval(myTest, 2);
function myTest() {
console.log("test")
}
Make sure to stop your setInterval function when not in use,
function myStopFunction() {
clearInterval(test);
}
Hello I cannot seem to figure out why the debounce function works as expected when passed directly to a keyup event; but it does not work if I wrap it inside an anonymous function.
I have fiddle of the problem: http://jsfiddle.net/6hg95/1/
EDIT: Added all the things I tried.
HTML
<input id='anonFunction'/>
<input id='noReturnAnonFunction'/>
<input id='exeDebouncedFunc'/>
<input id='function'/>
<div id='output'></div>
JAVASCRIPT
$(document).ready(function(){
$('#anonFunction').on('keyup', function () {
return _.debounce(debounceIt, 500, false); //Why does this differ from #function
});
$('#noReturnAnonFunction').on('keyup', function () {
_.debounce(debounceIt, 500, false); //Not being executed
});
$('#exeDebouncedFunc').on('keyup', function () {
_.debounce(debounceIt, 500, false)(); //Executing the debounced function results in wrong behaviour
});
$('#function').on('keyup', _.debounce(debounceIt, 500, false)); //This is working.
});
function debounceIt(){
$('#output').append('debounced');
}
anonFunction and noReturnAnonFunction does not fire the debounce function; but the last function does fire. I do not understand why this is. Can anybody please help me understand this?
EDIT
Ok, so the reason that the debounce does not happen in #exeDebouncedFunc (the one you refer) is because the function is executed in the scope of the anonymous function and another keyup event will create a new function in another anonymous scope; thus firing the debounced function as many times as you type something (instead of firing once which would be the expected behaviour; see beviour of #function)?
Can you please explain the difference between #anonFunction and the #function. Is this again a matter of scoping why one of them works and the other does not?
EDIT
Ok, so now I understand why this is happening. And here is why I needed to wrap it inside an anonymous function:
Fiddle: http://jsfiddle.net/6hg95/5/
HTML
<input id='anonFunction'/>
<div id='output'></div>
JAVASCRIPT
(function(){
var debounce = _.debounce(fireServerEvent, 500, false);
$('#anonFunction').on('keyup', function () {
//clear textfield
$('#output').append('clearNotifications<br/>');
debounce();
});
function fireServerEvent(){
$('#output').append('serverEvent<br/>');
}
})();
As Palpatim explained, the reason lies in the fact that _.debounce(...) returns a function, which when invoked does its magic.
Therefore in your #anonFunction example, you have a key listener, which when invoked does nothing but return a function to the invoker, which does nothing with the return values from the event listener.
This is a snippet of the _.debounce(...) definition:
_.debounce
function (func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
if (immediate && !timeout) func.apply(context, args);
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
Your key event listener must invoke the returned function from _.debounce(...), or you can do as in your non-anonymous example and use the returned function from the _.debounce(...) call as your event listener.
Think easier
_.debounce returns a debounced function!
So instead of thinking in terms of
$el.on('keyup'), function(){
_.debounce(doYourThing,500); //uh I want to debounce this
}
you rather call the debounced function instead
var doYourThingDebounced = _.debounce(doYourThing, 500); //YES, this will always be debounced
$el.on('keyup', doYourThingDebounced);
debounce doesn't execute the function, it returns a function with the debounciness built into it.
Returns
(Function): Returns the new debounced function.
So your #function handler is actually doing the Right Thing, by returning a function to be used by jQuery as a keyup handler. To fix your #noReturnAnonFunction example, you could simply execute the debounced function in the context of your function:
$('#noReturnAnonFunction').on('keyup', function () {
_.debounce(debounceIt, 500, false)(); // Immediately executes
});
But that's introducing a needless anonymous function wrapper around your debounce.
You can return the debounce function like this:
(function(){
var debounce = _.debounce(fireServerEvent, 500, false);
$('#anonFunction').on('keyup', function () {
//clear textfield
$('#output').append('clearNotifications<br/>');
return debounce();
});
function fireServerEvent(){
$('#output').append('serverEvent<br/>');
}
})();
Came across this while looking for a solution to calling a debounce with a trailing call, found this article which really helped me:
https://newbedev.com/lodash-debounce-not-working-in-react
specifically:
Solution for those who came here because throttle / debounce doesn't work >with FunctionComponent - you need to store debounced function via useRef():
export const ComponentName = (value = null) => {
const [inputValue, setInputValue] = useState(value);
const setServicesValue = value => Services.setValue(value);
const setServicesValueDebounced = useRef(_.debounce(setServicesValue, 1000));
const handleChange = ({ currentTarget: { value } }) => {
setInputValue(value);
setServicesValueDebounced.current(value);
};
return <input onChange={handleChange} value={inputValue} />;
};
More generally, if you want a debounce with a trailing behaviour (accounts for last click, or more likely last change on a select input), and a visual feedback on first click/change, you are faced with the same issue.
This does not work:
$(document).on('change', "#select", function() {
$('.ajax-loader').show();
_.debounce(processSelectChange, 1000);
});
This would be a solution:
$(document).on('change', "#select", function() {
$('.ajax-loader').show();
});
$(document).on('change', "#select", _.debounce(processSelectChange, 1000));
Preety straight forward question, though I can't find the answer anywhere
I tried these two ways:
setInterval(function(){object/*or this*/.method()},500)
and
setInterval('object/*or this*/.method()',500)
setInterval in fact expects a method as the first argument, though there is an alternative syntax where the first argument can be a string of code (not recommended by most)
If you're having issues with that code, it may have to do with the scope of 'this'
setInterval(function(){this.method()},500)
In the above code, 'this' will refer to the closure itself, and wouldn't be the same as 'this.method' occurring outside of that closure. For example, the following would work:
function MyClass() {
this.thingy = 'yep this is a thingy'
}
var myClass = new MyClass()
// Will log 'MyClass yep this is a thingy'
setInterval(function() { console.log('MyClass', myClass.thingy) }, 1000)
Whereas the following will not work (presuming instantiating the object and calling foo()):
function MyOtherClass() {
this.thingy = 'also a thingy'
}
// Will log 'MyOtherClass undefined'
MyOtherClass.prototype.foo = function() {
setInterval(function() { console.log('MyOtherClass', this.thingy) }, 1000)
}
The second example will work if we get around using 'this' within the closure (presuming instantiating the object and calling bar()):
MyOtherClass.prototype.bar = function() {
var that = this
setInterval(function() { console.log('MyOtherClass', that.thingy) }, 1000)
}
Also be sure that setInterval is being passed the name of a function:
setInterval(someFunction, 500)
rather than executing a function as an argument
setInterval(someFunction(), 500)
This last line of code is usually a mistake, unless someFunction() returns a function itself ;)
The difference between your 2 ways for passing a function to setInterval is whether you want to pass your function as refrence of just copy of it. Allow me to explain it by example:
-1 Referring(demo):
var obj = {
testMethod: function () {
console.log('function (testMethod): intial output');
}
}
setInterval(function () {
obj.testMethod()
}, 1000);
obj.testMethod = function () {
console.log('function (testMethod): changed output');
}
when you run this code, the result 'll be execution of the modified version of testMethod. Because here you dont copy the function! Instead, you refer to it. So whenever function implementation is changed, the last modified version is executed.
-2 Copying(demo):
var obj = {
testMethod: function () {
console.log('function (testMethod): intial output');
}
}
setInterval(obj.testMethod, 1000);
obj.testMethod = function () {
console.log('function (testMethod): changed output');
}
Here all you do is you are passing a copy of the last defined version of the function testMethod to setInterval. So whatever changes you do to testMethod, the result of setInterval will not be changed.
It's there a way to configure the setInterval method of javascript to execute the method immediately and then executes with the timer
It's simplest to just call the function yourself directly the first time:
foo();
setInterval(foo, delay);
However there are good reasons to avoid setInterval - in particular in some circumstances a whole load of setInterval events can arrive immediately after each other without any delay. Another reason is that if you want to stop the loop you have to explicitly call clearInterval which means you have to remember the handle returned from the original setInterval call.
So an alternative method is to have foo trigger itself for subsequent calls using setTimeout instead:
function foo() {
// do stuff
// ...
// and schedule a repeat
setTimeout(foo, delay);
}
// start the cycle
foo();
This guarantees that there is at least an interval of delay between calls. It also makes it easier to cancel the loop if required - you just don't call setTimeout when your loop termination condition is reached.
Better yet, you can wrap that all up in an immediately invoked function expression which creates the function, which then calls itself again as above, and automatically starts the loop:
(function foo() {
...
setTimeout(foo, delay);
})();
which defines the function and starts the cycle all in one go.
I'm not sure if I'm understanding you correctly, but you could easily do something like this:
setInterval(function hello() {
console.log('world');
return hello;
}(), 5000);
There's obviously any number of ways of doing this, but that's the most concise way I can think of.
I stumbled upon this question due to the same problem but none of the answers helps if you need to behave exactly like setInterval() but with the only difference that the function is called immediately at the beginning.
Here is my solution to this problem:
function setIntervalImmediately(func, interval) {
func();
return setInterval(func, interval);
}
The advantage of this solution:
existing code using setInterval can easily be adapted by substitution
works in strict mode
it works with existing named functions and closures
you can still use the return value and pass it to clearInterval() later
Example:
// create 1 second interval with immediate execution
var myInterval = setIntervalImmediately( _ => {
console.log('hello');
}, 1000);
// clear interval after 4.5 seconds
setTimeout( _ => {
clearInterval(myInterval);
}, 4500);
To be cheeky, if you really need to use setInterval then you could also replace the original setInterval. Hence, no change of code required when adding this before your existing code:
var setIntervalOrig = setInterval;
setInterval = function(func, interval) {
func();
return setIntervalOrig(func, interval);
}
Still, all advantages as listed above apply here but no substitution is necessary.
You could wrap setInterval() in a function that provides that behavior:
function instantGratification( fn, delay ) {
fn();
setInterval( fn, delay );
}
...then use it like this:
instantGratification( function() {
console.log( 'invoked' );
}, 3000);
Here's a wrapper to pretty-fy it if you need it:
(function() {
var originalSetInterval = window.setInterval;
window.setInterval = function(fn, delay, runImmediately) {
if(runImmediately) fn();
return originalSetInterval(fn, delay);
};
})();
Set the third argument of setInterval to true and it'll run for the first time immediately after calling setInterval:
setInterval(function() { console.log("hello world"); }, 5000, true);
Or omit the third argument and it will retain its original behaviour:
setInterval(function() { console.log("hello world"); }, 5000);
Some browsers support additional arguments for setInterval which this wrapper doesn't take into account; I think these are rarely used, but keep that in mind if you do need them.
Here's a simple version for novices without all the messing around. It just declares the function, calls it, then starts the interval. That's it.
//Declare your function here
function My_Function(){
console.log("foo");
}
//Call the function first
My_Function();
//Set the interval
var interval = window.setInterval( My_Function, 500 );
There's a convenient npm package called firstInterval (full disclosure, it's mine).
Many of the examples here don't include parameter handling, and changing default behaviors of setInterval in any large project is evil. From the docs:
This pattern
setInterval(callback, 1000, p1, p2);
callback(p1, p2);
is identical to
firstInterval(callback, 1000, p1, p2);
If you're old school in the browser and don't want the dependency, it's an easy cut-and-paste from the code.
I will suggest calling the functions in the following sequence
var _timer = setInterval(foo, delay, params);
foo(params)
You can also pass the _timer to the foo, if you want to clearInterval(_timer) on a certain condition
var _timer = setInterval(function() { foo(_timer, params) }, delay);
foo(_timer, params);
For someone needs to bring the outer this inside as if it's an arrow function.
(function f() {
this.emit("...");
setTimeout(f.bind(this), 1000);
}).bind(this)();
If the above producing garbage bothers you, you can make a closure instead.
(that => {
(function f() {
that.emit("...");
setTimeout(f, 1000);
})();
})(this);
Or maybe consider using the #autobind decorator depending on your code.
You can set a very small initial delay-time (e.g. 100) and set it to your desired delay-time within the function:
var delay = 100;
function foo() {
console.log("Change initial delay-time to what you want.");
delay = 12000;
setTimeout(foo, delay);
}
To solve this problem , I run the function a first time after the page has loaded.
function foo(){ ... }
window.onload = function() {
foo();
};
window.setInterval(function()
{
foo();
}, 5000);
This example builds on #Alnitak's answer, but uses await Promise for finer granularity of control within the loop cycle.
Compare examples:
let stillGoing = true;
(function foo() {
console.log('The quick brown fox did its thing');
if (stillGoing) setTimeout(foo, 5000);
})();
foo();
In the above example we call foo() and then it calls itself every 5 seconds.
But if, at some point in the future, we set stillGoing to false in order to stop the loop, we'll still get an extra log line even after we've issued the stop order. This is because at any given time, before we set stillGoing to false the current iteration will have already created a timeout to call the next iteration.
If we instead use await Promise as the delay mechanism then we have an opportunity to stop the loop before calling the next iteration:
let stillGoing = true;
(async function foo() {
console.log('The quick brown fox did its thing');
await new Promise(resolve => setTimeout(resolve, 5000));
if (stillGoing) foo();
})();
foo();
In the second example we start by setting a 5000ms delay, after which we check the stillGoing value and decide whether calling another recursion is appropriate.
So if we set stillGoing to false at any point, there won't be that one extra log line printed after we set the value.
The caveat is this requires the function to be async, which may or may not be an option for a given use.
For Those using React, here is how I solve this problem:
const intervalRef = useRef(0);
useEffect(() => {
if (condition is true){
if (intervalRef.current === 0) {
callMyFunction();
}
const interval = setInterval(() => {
callMyFunction();
}, 5_000);
intervalRef.current = interval;
} else {
clearInterval(intervalRef.current);
}
}, [deps]);
// YCombinator
function anonymous(fnc) {
return function() {
fnc.apply(fnc, arguments);
return fnc;
}
}
// Invoking the first time:
setInterval(anonymous(function() {
console.log("bar");
})(), 4000);
// Not invoking the first time:
setInterval(anonymous(function() {
console.log("foo");
}), 4000);
// Or simple:
setInterval(function() {
console.log("baz");
}, 4000);
Ok this is so complex, so, let me put it more simple:
function hello(status ) {
console.log('world', ++status.count);
return status;
}
setInterval(hello, 5 * 1000, hello({ count: 0 }));
If you can use RxJS, there is something called timer():
import { Subscription, timer } from 'rxjs';
const INITIAL_DELAY = 1;
const INTERVAL_DELAY = 10000;
const timerSubscription = timer(INITIAL_DELAY, INTERVAL_DELAY)
.subscribe(() => {
this.updateSomething();
});
// when destroying
timerSubscription.unsubscribe();
With ES2017, it may be preferable to avoid setInterval altogether.
The following solution has a much cleaner execution flow, prevents issues if the function takes longer than the desired time to complete, and allows for asynchronous operations.
const timeout = (delayMs) => new Promise((res, _rej) => setTimeout(res, delayMs));
const DELAY = 1_000;
(async () => {
while (true) {
let start_time = Date.now();
// insert code here...
let end_time = Date.now();
await timeout(DELAY - (end_time - start_time));
}
})();
There's a problem with immediate asynchronous call of your function, because standard setTimeout/setInterval has a minimal timeout about several milliseconds even if you directly set it to 0. It caused by a browser specific work.
An example of code with a REAL zero delay wich works in Chrome, Safari, Opera
function setZeroTimeout(callback) {
var channel = new MessageChannel();
channel.port1.onmessage = callback;
channel.port2.postMessage('');
}
You can find more information here
And after the first manual call you can create an interval with your function.
actually the quickest is to do
interval = setInterval(myFunction(),45000)
this will call myfunction, and then will do it agaian every 45 seconds which is different than doing
interval = setInterval(myfunction, 45000)
which won't call it, but schedule it only