簡體   English   中英

如何停止 setTimeout 循環?

[英]How to stop a setTimeout loop?

我正在嘗試用圖像精靈構建一個加載指示器,我想出了這個功能

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(); 在 else 中保持循環運行,所以現在我的問題是如何在我想要 [加載完成] 時停止這個循環?

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停止事情本身,通過檢測一些完成條件已經得到滿足。

我知道這是一個老問題,無論如何我想發布我的方法。 這樣你就不必處理 TJ Crowder 解釋的 0 把戲。

var keepGoing = true;

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

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

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

function stopLoop() {
    keepGoing = false;
}

處理超時循環的最簡單方法

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

您需要使用一個變量來跟蹤“完成度”,然后在循環的每次迭代中對其進行測試。 如果 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

由於這是用 extjs 標簽標記的,因此可能值得查看 extjs 方法: http : //docs.sencha.com/extjs/6.2.0/classic/Ext.Function.html#method-interval

這很像 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();

當你想停止時,你可以使用clearInterval來停止它

clearInterval(bgPositionTimer);

一個示例用例是:

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

如果您想從函數內部停止循環,請嘗試以下操作:

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

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

您也可以將其包裝在另一個函數中,只需確保您有一個計時器變量並使用 clearInterval(theTimerVariable) 來停止循環

在最佳答案中,我認為if (timer)語句被錯誤地放置在stop()函數調用中。 它應該像if (timer) timer = setTimeout(run, 200)一樣放在run()函數調用中。 這可以防止在調用stop()之后立即運行setTimeout語句。

編輯 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:這已經過測試並且按預期工作。

當任務完成並且您可以顯示任務(在您的情況下為圖像)時,下次刷新時不要發送 javascript。 如果您的服務器使用 PHP。

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

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

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