如何停止setTimeout循环?

时间:2011-12-09 08:58:20

标签: javascript css extjs settimeout

我正在尝试用图像精灵构建一个加载指示器,我想出了这个函数

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

所以输出看起来像这样

http://jsfiddle.net/TTkre/

我不得不使用setBgPosition();在其他内部保持这个循环运行所以现在我的问题是如何在我想要[加载完成]后停止这个循环?

8 个答案:

答案 0 :(得分:82)

setTimeout返回一个计时器句柄,您可以使用该句柄来clearTimeout停止超时。

例如:

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

所以你将其用作:

var stop = setBgPosition();
// ...later, when you're ready to stop...
stop();

请注意,我不是让setBgPosition再次召唤自己,而是将c设置回0。否则,这不会起作用。另请注意,我已将0用作超时暂停时的句柄值; 0不是来自setTimeout的有效返回值,因此它会生成一个方便的标记。

这也是我认为你用setInterval而不是setTimeout改善的(少数)地方之一。 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 setInterval(run, 200);
}

像这样使用:

var timer = setBgPosition();
// ...later, when you're ready to stop...
clearInterval(timer);

尽管如此,我还是希望通过检测某些完成条件已经满足来找到一种让setBgPosition停止本身的方法。

答案 1 :(得分:10)

我知道这是一个老问题,无论如何我想发布我的方法。这样你就不必处理T. J. Crowder所提出的0技巧了。

var keepGoing = true;

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

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

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

function stopLoop() {
    keepGoing = false;
}

答案 2 :(得分:2)

您需要使用变量来跟踪“完成度”,然后在循环的每次迭代中对其进行测试。如果完成== 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

答案 3 :(得分:2)

以最简单的方式处理超时循环

function myFunc (terminator = false) {
    if(terminator) {
        clearTimeout(timeOutVar);
    } else {
        // do something
        timeOutVar = setTimeout(function(){myFunc();}, 1000);
    }
}   
myFunc(true); //  -> start loop
myFunc(false); //  -> end loop

答案 4 :(得分:2)

var myVar = null;

if(myVar)
   clearTimeout(myVar);

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

答案 5 :(得分:1)

如果要从函数内部停止循环,请尝试以下操作:

let timer = setInterval(function(){
  // Have some code to do something

  if(/*someStopCondition*/){ 
    clearInterval(timer)
  }
},1000);

您还可以将其包装在另一个函数中,只需确保具有计时器变量并使用clearInterval(theTimerVariable)即可停止循环

答案 6 :(得分:0)

我不确定,但可能是你想要的:

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

答案 7 :(得分:0)

在最上面的答案中,我认为if (timer)语句被错误地放在了stop()函数调用中。应该将其放在run()之类的if (timer) timer = setTimeout(run, 200)函数调用中。这样可以防止在调用setTimeout之后立即运行将来的stop()语句。

编辑2: 对于同步函数调用,最佳答案是正确的。如果要进行异步函数调用,请改用我的。

下面给出的示例是我认为正确的方法(如果我做错了,可以纠正我),因为我尚未测试过此方法:

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

编辑1: 这已经过测试,可以按预期工作。