'How to stop a setTimeout loop?
I'm trying to build a loading indicator with a image sprite and I came up with this function
function setBgPosition() {
var c = 0;
var numbers = [0, -120, -240, -360, -480, -600, -720];
function run() {
Ext.get('common-spinner').setStyle('background-position', numbers[c++] + 'px 0px');
if (c<numbers.length)
{
setTimeout(run, 200);
}else
{
setBgPosition();
}
}
setTimeout(run, 200);
}
so the out put is looks like this
I had to use setBgPosition(); inside else to keep this running in a loop so now my problem is how to stop this loop once I want [load finished]?
Solution 1:[1]
setTimeout
returns a timer handle, which you can use to stop the timeout with clearTimeout
.
So for instance:
function setBgPosition() {
var c = 0,
timer = 0;
var numbers = [0, -120, -240, -360, -480, -600, -720];
function run() {
Ext.get('common-spinner').setStyle('background-position', numbers[c++] + 'px 0px');
if (c >= numbers.length) {
c = 0;
}
timer = setTimeout(run, 200);
}
timer = setTimeout(run, 200);
return stop;
function stop() {
if (timer) {
clearTimeout(timer);
timer = 0;
}
}
So you'd use that as:
var stop = setBgPosition();
// ...later, when you're ready to stop...
stop();
Note that rather than having setBgPosition
call itself again, I've just had it set c
back to 0
. Otherwise, this wouldn't work. Also note that I've used 0
as a handle value for when the timeout isn't pending; 0
isn't a valid return value from setTimeout
so it makes a handy flag.
This is also one of the (few) places I think you'd be better off with setInterval
rather than setTimeout
. setInterval
repeats. So:
function setBgPosition() {
var c = 0;
var numbers = [0, -120, -240, -360, -480, -600, -720];
function run() {
Ext.get('common-spinner').setStyle('background-position', numbers[c++] + 'px 0px');
if (c >= numbers.length) {
c = 0;
}
}
return setInterval(run, 200);
}
Used like this:
var timer = setBgPosition();
// ...later, when you're ready to stop...
clearInterval(timer);
All of the above notwithstanding, I'd want to find a way to make setBgPosition
stop things itself, by detecting that some completion condition has been satisfied.
Solution 2:[2]
I know this is an old question, I'd like to post my approach anyway. This way you don't have to handle the 0 trick that T. J. Crowder expained.
var keepGoing = true;
function myLoop() {
// ... Do something ...
if(keepGoing) {
setTimeout(myLoop, 1000);
}
}
function startLoop() {
keepGoing = true;
myLoop();
}
function stopLoop() {
keepGoing = false;
}
Solution 3:[3]
SIMPLIEST WAY TO HANDLE TIMEOUT LOOP
function myFunc (terminator = false) {
if(terminator) {
clearTimeout(timeOutVar);
} else {
// do something
timeOutVar = setTimeout(function(){myFunc();}, 1000);
}
}
myFunc(true); // -> start loop
myFunc(false); // -> end loop
Solution 4:[4]
You need to use a variable to track "doneness" and then test it on every iteration of the loop. If done == true then return.
var done = false;
function setBgPosition() {
if ( done ) return;
var c = 0;
var numbers = [0, -120, -240, -360, -480, -600, -720];
function run() {
if ( done ) return;
Ext.get('common-spinner').setStyle('background-position', numbers[c++] + 'px 0px');
if (c<numbers.length)
{
setTimeout(run, 200);
}else
{
setBgPosition();
}
}
setTimeout(run, 200);
}
setBgPosition(); // start the loop
setTimeout( function(){ done = true; }, 5000 ); // external event to stop loop
Solution 5:[5]
var myVar = null;
if(myVar)
clearTimeout(myVar);
myVar = setTimeout(function(){ alert("Hello"); }, 3000);
Solution 6:[6]
Try something like this in case you want to stop the loop from inside the function:
let timer = setInterval(function(){
// Have some code to do something
if(/*someStopCondition*/){
clearInterval(timer)
}
},1000);
You can also wrap this inside a another function, just make sure you have a timer variable and use clearInterval(theTimerVariable) to stop the loop
Solution 7:[7]
As this is tagged with the extjs tag it may be worth looking at the extjs method: http://docs.sencha.com/extjs/6.2.0/classic/Ext.Function.html#method-interval
This works much like setInterval, but also takes care of the scope, and allows arguments to be passed too:
function setBgPosition() {
var c = 0;
var numbers = [0, -120, -240, -360, -480, -600, -720];
function run() {
Ext.get('common-spinner').setStyle('background-position', numbers[c++] + 'px 0px');
if (c<numbers.length){
c=0;
}
}
return Ext.Function.interval(run,200);
}
var bgPositionTimer = setBgPosition();
when you want to stop you can use clearInterval
to stop it
clearInterval(bgPositionTimer);
An example use case would be:
Ext.Ajax.request({
url: 'example.json',
success: function(response, opts) {
clearInterval(bgPositionTimer);
},
failure: function(response, opts) {
console.log('server-side failure with status code ' + response.status);
clearInterval(bgPositionTimer);
}
});
Solution 8:[8]
I am not sure, but might be what you want:
var c = 0;
function setBgPosition()
{
var numbers = [0, -120, -240, -360, -480, -600, -720];
function run()
{
Ext.get('common-spinner').setStyle('background-position', numbers[c++] + 'px 0px');
if (c<=numbers.length)
{
setTimeout(run, 200);
}
else
{
Ext.get('common-spinner').setStyle('background-position', numbers[0] + 'px 0px');
}
}
setTimeout(run, 200);
}
setBgPosition();
Solution 9:[9]
In the top answer, I think the if (timer)
statement has been mistakenly placed within the stop()
function call. It should instead be placed within the run()
function call like if (timer) timer = setTimeout(run, 200)
. This prevents future setTimeout
statements from being run right after stop()
is called.
EDIT 2: The top answer is CORRECT for synchronous function calls. If you want to make async function calls, then use mine instead.
Given below is an example with what I think is the correct way (feel to correct me if I am wrong since I haven't yet tested this):
const runSetTimeoutsAtIntervals = () => {
const timeout = 1000 // setTimeout interval
let runFutureSetTimeouts // Flag that is set based on which cycle continues or ends
const runTimeout = async() => {
await asyncCall() // Now even if stopRunSetTimeoutsAtIntervals() is called while this is running, the cycle will stop
if (runFutureSetTimeouts) runFutureSetTimeouts = setTimeout(runTimeout, timeout)
}
const stopRunSetTimeoutsAtIntervals = () => {
clearTimeout(runFutureSetTimeouts)
runFutureSetTimeouts = false
}
runFutureSetTimeouts = setTimeout(runTimeout, timeout) // Set flag to true and start the cycle
return stopRunSetTimeoutsAtIntervals
}
// You would use the above function like follows.
const stopRunSetTimeoutsAtIntervals = runSetTimeoutsAtIntervals() // Start cycle
stopRunSetTimeoutsAtIntervals() // Stop cycle
EDIT 1: This has been tested and works as expected.
Solution 10:[10]
When the task is completed and you can display the task (image in your case), on the next refresh don't send the javascript. If your server is using PHP.
<?php if (!$taskCompleted) { ?>
<script language="javascript">
setTimeout(function(){
window.location.reload(1);
}, 5000);
</script>
<?php } ?>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | ThimoKl |
Solution 3 | VALIKHAN |
Solution 4 | Neil Essy |
Solution 5 | Mahdi Bashirpour |
Solution 6 | Oyebola |
Solution 7 | Theo |
Solution 8 | |
Solution 9 | |
Solution 10 | Senthil |