在循环中暂停并重新启动setTimeout。

问题描述 投票:0回答:1

我在我的循环中设置了一个延迟,通过等待一个函数返回一个 PromisesetTimeout(). 我能够启动和重置循环,但也希望能够以某种方式暂停循环。在再次点击启动按钮后,我想从数组中最后一个循环的元素开始继续循环。

$(document).ready(() => {
    $('#btn-start').click(() => {
        loopMoves();
    });
    $('#btn-pause').click(() => {
        // ...
    });
    $('#btn-reset').click(() => {
        clearTimeout(timer);
    });
})

var moves = ['Nf3', 'd5', 'g3', 'g6', 'c4', 'dxc4'];

async function loopMoves() {
    for(let i = 0; i < moves.length; i++){
        console.log(moves[i]);
        await delay(2000);
    }
}

var timer;
function delay(ms) {
    return new Promise((x) => {
        timer = setTimeout(x, ms);
    });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="btn-start">start</button>
<button id="btn-pause">pause</button>
<button id="btn-reset">reset</button>
javascript jquery arrays loops settimeout
1个回答
1
投票

可以通过使用setInterval()和一个生成器函数来代替for循环。

请参考下面的示例代码。

$(document).ready(() => {
    let loop = loopMoves();
    $('#btn-start').click(() => {
      console.log("****STARTED*****");
      loop("PLAY");
    });
    $('#btn-pause').click(() => {
        console.log("****PAUSED*****");
        loop("PAUSE");
    });
    $('#btn-reset').click(() => {
      console.log("****RESET*****");
      loop("RESET");
    });
})

const moves = ['Nf3', 'd5', 'g3', 'g6', 'c4', 'dxc4'];

function loopMoves() {
    let moves = generateMoves();
    let intervalId;
      function startInterval(){
        intervalId = setInterval(()=>{
        const move = moves.next();
          if(move.done) 
            clearInterval(intervalId);
          else
            console.log(move.value);
        }, 2000);
      }
    return (state) => {
      switch(state){
        case "PLAY": startInterval(); break;
        case "PAUSE": clearInterval(intervalId); break;
        case "RESET": moves = generateMoves();
                      clearInterval(intervalId); break;
      }
    }
}

function* generateMoves(){
  for(let i = 0; i < moves.length; i++){
    yield moves[i];
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="btn-start">start</button>
<button id="btn-pause">pause</button>
<button id="btn-reset">reset</button>

1
投票

你可以考虑把事情翻转到更多的基于时间的 "状态机"。

当点击播放时,你开始你的定时器("播放 "状态),在定时器结束时你检查当前状态。如果状态仍然是 "播放",你抓住下一步棋,并显示,并再次启动定时器。如果没有交互发生,这将重复,直到所有的动作都完成,状态进入 "停止"。如果有人点击暂停,你将进入 "暂停 "状态,但剩下的就保持原样。当再次点击 "播放 "时(从 "暂停 "到 "播放"),你只需回到原来的位置。当点击 "停止 "时,你会重置一切。当从 "停止 "状态点击播放时,你将移动指数设置为0,并进行第一步移动,启动计时器。

这确实意味着每次更新只发生在每2秒,但这可能是可以接受的,它比暂停定时器等替代方案要简单得多。

const moves = ['Nf3', 'd5', 'g3', 'g6', 'c4', 'dxc4']; // These can be loaded however
let state = "stopped"; // The starting state
let currentMoveIndex = 0; // Used to track which move to grab for the next tick

$(document).ready(() => {
    $('#btn-start').click(() => {
        state = "playing";
        tick();
    });
    $('#btn-pause').click(() => {
        state = "paused";
    });
    $('#btn-reset').click(() => {
        state = "stopped";
        tick(); // Since we put the clean up inside the tick function we run it again to be sure it's executed in case it was paused
    });
})

function tick() {
    switch(state) {
        case "playing":
            if (currentMoveIndex + 1 === moves.length) {
                break;
            }

            const move = moves[currentMoveIndex];
            console.log(move); // Or whatever you wish to do with the move
            currentMoveIndex += 1;
            setTimeout(tick, 2000);
            break;
        case "paused":
            // Do whatever you want based on entering the paused state,
            // Maybe show some message saying "Paused"?
            break;
        case "stopped":
            currentMoveIndex = 0;
            break;
    }
}

如果你使用的是React或Angular,你可以把这个包进一个ComponentController中,以保持事情的一致性,如果你使用的是纯JavaScript,则可以把它全部包进一个Game函数中。


0
投票

我试过这几个答案,似乎都可以,谢谢。我回到这个问题上,是因为我之前想到了一些东西,想得到一些反馈。如果我将循环的项目存储在一个新的数组中,然后从最后存储的项目设置的位置重新开始循环,会怎么样呢?i = done.length (done 是循环项的数组)。) 当点击 暂停 扣子 clearTimeout() 被调用,当点击 重置 按钮,我清除了 done 数组后,调用 clearTimeout(), 开始 仍然运行包含循环的函数。

我理解我并不是真的暂停定时器,而是停止定时器并从数组的最后一个位置重新开始。这是否会被认为是 "不好的做法",或者这是否也是一个可以接受的解决方案?

$(document).ready(() => {
    $('#btn-start').click(() => {
        loopMoves();
    });
    $('#btn-pause').click(() => {
        clearTimeout(timer);
    });
    $('#btn-reset').click(() => {
        clearTimeout(timer);
        done = [];
    });
})

var moves = ['Nf3', 'd5', 'g3', 'g6', 'c4', 'dxc4'];
var done = [];

async function loopMoves() {
    for(let i = done.length; i < moves.length; i++){
        console.log(moves[i]);
        done.push(moves[i]);
        await delay(2000);
    }
}

var timer;
function delay(ms) {
    return new Promise((x) => {
        timer = setTimeout(x, ms);
    });
}
© www.soinside.com 2019 - 2024. All rights reserved.