简体   繁体   English

如何停止 setTimeout 循环?

[英]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所以输出看起来像这样

http://jsfiddle.net/TTkre/ http://jsfiddle.net/TTkre/

I had to use setBgPosition();我不得不使用 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]?在 else 中保持循环运行,所以现在我的问题是如何在我想要 [加载完成] 时停止这个循环?

setTimeout returns a timer handle, which you can use to stop the timeout with clearTimeout . setTimeout返回一个计时器句柄,您可以使用它来通过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 .请注意,我没有让setBgPosition再次调用自身,而是将c重新设置为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作为句柄值; 0 isn't a valid return value from setTimeout so it makes a handy flag. 0不是setTimeout的有效返回值,因此它是一个方便的标志。

This is also one of the (few) places I think you'd be better off with setInterval rather than setTimeout .这也是我认为最好使用setInterval而不是setTimeout的(少数)地方之一。 setInterval repeats. setInterval重复。 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.尽管如此,我还是想找到一种方法来让setBgPosition停止事情本身,通过检测一些完成条件已经得到满足。

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 TJ Crowder expained.这样你就不必处理 TJ Crowder 解释的 0 把戏。

var keepGoing = true;

function myLoop() {
    // ... Do something ...

    if(keepGoing) {
        setTimeout(myLoop, 1000);
    }
}

function startLoop() {
    keepGoing = true;
    myLoop();
}

function stopLoop() {
    keepGoing = false;
}

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
var myVar = null;

if(myVar)
   clearTimeout(myVar);

myVar = setTimeout(function(){ alert("Hello"); }, 3000);

You need to use a variable to track "doneness" and then test it on every iteration of the loop.您需要使用一个变量来跟踪“完成度”,然后在循环的每次迭代中对其进行测试。 If done == true then return.如果 done == true 则返回。

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

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由于这是用 extjs 标签标记的,因此可能值得查看 extjs 方法: 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:这很像 setInterval,但也负责作用域,并允许传递参数:

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来停止它

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);
    }
});

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您也可以将其包装在另一个函数中,只需确保您有一个计时器变量并使用 clearInterval(theTimerVariable) 来停止循环

In the top answer, I think the if (timer) statement has been mistakenly placed within the stop() function call.在最佳答案中,我认为if (timer)语句被错误地放置在stop()函数调用中。 It should instead be placed within the run() function call like if (timer) timer = setTimeout(run, 200) .它应该像if (timer) timer = setTimeout(run, 200)一样放在run()函数调用中。 This prevents future setTimeout statements from being run right after stop() is called.这可以防止在调用stop()之后立即运行setTimeout语句。

EDIT 2: The top answer is CORRECT for synchronous function calls.编辑 2:对于同步函数调用,最佳答案是正确的。 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.编辑 1:这已经过测试并且按预期工作。

When the task is completed and you can display the task (image in your case), on the next refresh don't send the javascript.当任务完成并且您可以显示任务(在您的情况下为图像)时,下次刷新时不要发送 javascript。 If your server is using PHP.如果您的服务器使用 PHP。

<?php if (!$taskCompleted) { ?>
<script language="javascript">
setTimeout(function(){
   window.location.reload(1);
}, 5000);
</script>
<?php } ?>

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();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM