我在我的循环中设置了一个延迟,通过等待一个函数返回一个 Promise
与 setTimeout()
. 我能够启动和重置循环,但也希望能够以某种方式暂停循环。在再次点击启动按钮后,我想从数组中最后一个循环的元素开始继续循环。
$(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>
可以通过使用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>
你可以考虑把事情翻转到更多的基于时间的 "状态机"。
当点击播放时,你开始你的定时器("播放 "状态),在定时器结束时你检查当前状态。如果状态仍然是 "播放",你抓住下一步棋,并显示,并再次启动定时器。如果没有交互发生,这将重复,直到所有的动作都完成,状态进入 "停止"。如果有人点击暂停,你将进入 "暂停 "状态,但剩下的就保持原样。当再次点击 "播放 "时(从 "暂停 "到 "播放"),你只需回到原来的位置。当点击 "停止 "时,你会重置一切。当从 "停止 "状态点击播放时,你将移动指数设置为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函数中。
我试过这几个答案,似乎都可以,谢谢。我回到这个问题上,是因为我之前想到了一些东西,想得到一些反馈。如果我将循环的项目存储在一个新的数组中,然后从最后存储的项目设置的位置重新开始循环,会怎么样呢?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);
});
}