跳出setTimeout循环

19

我正在尝试跳出一个setTimeout循环,但遇到了一些问题。

for (var i = 0; i < 75; i++) {
  setTimeout(function (i) {
    return function () {
      console.log("turn no. " + i);
      if (table.game.playerWon) {
        console.log('Player won');
        // I want to stop the loop now
        // i = 75; didn't work
      }
    };
  }(i), 100 * i);
}

我已经读了大约100篇有关setTimeout的文章,但是无法弄清楚这个问题。

编辑:

让我稍微澄清一下我的目标。

我的游戏有75个回合,每个回合应该花费大约500毫秒,在回合期间,我想要检查是否满足条件并宣布玩家获胜,在玩家获胜后,没有必要继续其余的回合。


2
你知道你设置了75个并行超时吗? - Ilan Frumer
1
将setTimeout分配给一个变量,然后使用clearTimeout(该变量)取消它。 - brbcoding
1
你误解了异步性。在你的任何一个setTimeout调用运行之前,循环已经完成了。因此,你一次性安排了75个事件,然后它们将开始触发。 - Sean Vieira
希望你能清楚地意识到setIntervalsetTimeout之间的区别。 - Ilan Frumer
setInterval 每隔 x 时间执行一次,而 setTimeout 则等待 x 时间后执行,如果我没记错的话。 - Martijn
显示剩余3条评论
4个回答

23

不要设置那么多定时器,使用setInterval创建一个连续的定时器:

var counter = 0;

var timer = setInterval(function () {

    console.log("turn no. " + counter);

    if (table.game.playerWon) {
        console.log('Player won');
    }

    if (counter >= 75 || table.game.playerWon) {
        clearInterval(timer);
    }

    counter++;

}, 100);

如果你的轮次需要500毫秒,请将最后的100更改为500


这个答案立即起作用了。非常感谢。被Stackoverflow的回答速度惊呆了!我会尽快接受这个答案。 - Martijn

13

不应使用for循环,而是使用递归的setTimeout

setInterval对于很多事情来说并不合适:

  • 如果出现错误,则无法停止计时器。
  • 如果需要不同的执行时间步骤。
  • 如果需要在链内传递数据。
  • 如果需要进行异步操作。
  • 更糟糕的是 - SETINTERVAL不能保证执行
  • 因此,仅在明确知道自己在做什么时才使用它!

解决方案:

var func = function(i){

    return function(){
        if (i >= 75) return;
        console.log("turn no. " + i);

        if(table.game.playerWon) {
            console.log('Player won');
        } else {
            setTimeout(func(++i), 500); 
        }

    }   
}

setTimeout(func(0), 500); 

如果想要检查它的工作原理,您可以在node.js中运行它:

var winTurn = 10;

var func = function(i){

    return function(){
        if (i >= 75) return;
        console.log("turn no. " + i);

        if(i === winTurn) {
            console.log('Player won');
        } else {
            setTimeout(func(++i), 50); 
        }

    }   
}

setTimeout(func(1), 50); 

3
我在这里看不到递归。可能是一种重复出现的技术。此外,这种技术往往会向时钟添加漂移,在这种情况下似乎不太理想。 - kuroi neko
2
这篇博客文章的语气非常教条主义。看起来作者对异步执行感到不安,而且过于固执于Ajax调用(顺便说一下,这些调用不需要任何超时)。在我看来,在尝试处理周期性或单次定时器的异步执行之前,最好先了解自己在做什么。 - kuroi neko
1
不管是教条主义还是非教条主义,你都不能忽视这些问题。此外,在node.js中,每一件小事都是异步的(不仅仅是AJAX),并且可能有太多的延迟。 - Ilan Frumer
你不能停止火车(笑),并点赞。这帮助了我解决问题的想法,也很有启发性。 - Green

5
我认为最好使用setInterval而不是setTimeout。
并且为了清除它们,你可以将它们分配给一个变量,然后使用clearTimeout来清除超时。
var myVar = setTimeout(func, time);
var myVar2 = setInterval(func, time);

clearTimeout(myVar);
clearInterval(myVar2);

0

这是一个你应该写的例子

var timeouts = [];
for (var i = 0; i < 75; i++) {

    timeouts[i] = setTimeout(function(i) {

        return function() {

            console.log("turn no. " + i);

            if(table.game.playerWon) {
                console.log('Player won');
                // I want to stop the loop now
                for(var k = i; k < timeouts.length; k++) {
                    clearTimeout(timeouts[i]);
                }
            }

        };

    }(i), 100*i);

}

另一个更简单的解决方法是仅在 table.game.playerWon 为 false 时调用 setTimeout
var timeoutFunction = function(i) {

    return function() {

        console.log("turn no. " + i);

        if(table.game.playerWon) {
            console.log('Player won');
            // Game stopped
        } else {
            timeout = setTimeout(timeoutFunction(i+1), 100);  
        }
    };

}

var timeout = setTimeout(timeoutFunction(0), 100);

希望能有所帮助


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接