在循环中暂停并重新启动 setTimeout
Posted
技术标签:
【中文标题】在循环中暂停并重新启动 setTimeout【英文标题】:Pause and restart setTimeout in loop 【发布时间】:2020-09-24 01:25:44 【问题描述】:我通过等待返回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>
【问题讨论】:
【参考方案1】:这可以通过使用 setInterval() 和生成器函数来实现,而不是使用 for 循环。
请参考下面的 sn-p 工作示例:
$(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>
【讨论】:
【参考方案2】:您可能会考虑将事物翻转为更多基于时间的“状态机”。
点击播放时,您将启动计时器(“正在播放”状态),在计时器结束时检查当前状态。如果状态仍在“播放”,则抓住下一步并显示并再次启动计时器。如果没有发生交互,则重复此操作,直到完成所有移动并且状态变为“停止”。 如果有人单击暂停,您将进入“暂停”状态,但其余部分保持原样。当再次单击播放时(从“暂停”到“播放”),您只需回到原来的位置。单击停止时,您将重置所有内容。当从“停止”状态单击播放时,您将移动索引设置为 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 之类的东西,您可以将其包装到 Component/Controller 中以将它们组合在一起,或者如果您使用纯 javascript,则可以将它们全部包装到 Game 函数中
【讨论】:
【参考方案3】:我已经尝试了这些答案,它们似乎都有效,谢谢。我回到这个问题是因为我之前想到了一些东西,并希望得到一些反馈。如果我将循环项目存储在一个新数组中并从最后存储的项目设置i = done.length
(done
是循环项目数组)的位置重新启动循环会怎样。当点击 pause 按钮 clearTimeout()
被调用,当点击 reset 按钮时,我清除了 @ 987654324@数组调用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);
);
【讨论】:
以上是关于在循环中暂停并重新启动 setTimeout的主要内容,如果未能解决你的问题,请参考以下文章
如何在 jquery transit 中暂停和重新启动 css3 转换
如何使用CoffeeScript重新同步setTimeout()? Rails 5