如何实现循环计数器与顺序执行的双倒计时定时器
解决顺序定时器与循环次数显示问题
核心问题分析
原代码的问题在于:
for循环是同步执行的,瞬间完成所有迭代,直接把循环次数设为最终值,同时启动所有定时器,导致所有倒计时同时运行RunTimer是无阻塞的异步函数,无法直接控制执行顺序
解决方案代码
修改后的完整可运行代码如下:
<!doctype html> <html lang="en"> <head> <!-- Required meta tags --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <!-- Bootstrap CSS --> <link rel="stylesheet" href="/static/css/bootstrap.min.css"> <title> Timer Scherm </title> </head> <body> <div class="container"> <h1> Timer Scherm </h1> </br> <div class="row mb-3 text-left"> <div class="col-6 col-sm-1 themed-grid-col"><h5>Step: 3</h5></div> <div class="col-6 col-sm-4 themed-grid-col"><h5>Fase: 15m 3 Sets Rapid</h5></div> <div class="col-6 col-sm-2 themed-grid-col"><h5>Edge: 3.0 sec.</h5></div> <div class="col-6 col-sm-2 themed-grid-col"><h5>Face: 6.0 sec.</h5></div> <div class="col-6 col-sm-2 themed-grid-col"><h5>Reps: 3 times</h5></div> </div> <h1 class="text-primary"> <p id="RepeatCounter"></p> </h1> </br> <h1 class="text-danger"> <div id="countdownEdge"></div> </h1> </br> <h1 class="text-success"> <div id="countdownFace"></div> </h1> <script> const Repeats = 3; const timeleftEdge = 3.0; const timeleftFace = 6.0; // 启动整个流程 startTimerSequence(); // 异步函数,控制整个定时器序列 async function startTimerSequence() { for (let index = 1; index <= Repeats; index++) { // 更新当前循环次数 document.getElementById("RepeatCounter").innerHTML = "Repeat : " + index.toFixed(0) + " time"; // 先执行Edge定时器,等待它结束 await RunTimer(timeleftEdge, 'countdownEdge', 'Edge'); // 再执行Face定时器,等待它结束 await RunTimer(timeleftFace, 'countdownFace', 'Face'); } } // 改造为返回Promise的定时器函数,便于await等待结束 function RunTimer(totalTime, DIV_ID, Type) { return new Promise((resolve) => { let timeleft = totalTime; // 用局部变量保存时间,避免闭包问题 const Timer = setInterval(function(){ if(timeleft <= 0){ clearInterval(Timer); document.getElementById(DIV_ID).innerHTML = "Ready"; resolve(); // 定时器结束,resolve Promise } else { document.getElementById(DIV_ID).innerHTML = Type + " : " + timeleft.toFixed(1) + " seconds remaining"; } timeleft -= 0.1; }, 100); }); } </script> </div> <!-- Optional JavaScript --> <!-- jQuery first, then Popper.js, then Bootstrap JS --> <script src="/static/js/jquery-3.7.1.slim.min.js"></script> <script src="/static/js/popper.min.js"></script> <script src="/static/js/bootstrap.min.js"></script> </body> </html>
关键改动说明
RunTimer改造为Promise函数- 用
new Promise包裹定时器逻辑,当倒计时结束时调用resolve(),标记该定时器任务完成 - 将
timeleft改为函数内部的局部变量,避免原代码中闭包导致的时间变量共享问题
- 用
异步循环控制
- 创建
startTimerSequence异步函数,结合await确保每次循环中:- 先更新当前循环次数
- 等待Edge定时器完全结束后,再启动Face定时器
- 两个定时器都结束后,再进入下一次循环
- 创建
循环次数正确显示
- 循环异步执行,每次迭代都会先更新
RepeatCounter内容,再执行定时器,因此会逐步显示当前循环次数,而非直接跳到最终值
- 循环异步执行,每次迭代都会先更新
适配数据库数据的版本
如果要替换为数据库获取的变量,只需修改初始变量:
const Repeats = {{ fase['rep'] }}; const timeleftEdge = {{ fase['edge'] }}; const timeleftFace = {{ fase['face'] }};
内容的提问来源于stack exchange,提问作者Aleg Moi
相关产品推荐
相关产品推荐

