本文深入剖析 Simon 游戏开发中 roundList 数组长度异常翻倍的根源——重复调用 setInterval 而未清理前序定时器,并提供可直接落地的修复方案与最佳实践,帮助开发者彻底解决该问题。
说实话,这个 bug 在 Simon 游戏开发中非常常见——roundList 每轮本应只追加一个随机颜色,例如 ['green', 'red'],但运行后却莫名其妙变成 ['green', 'red', 'green', 'red'],直接翻倍。许多开发者第一反应是排查 .push() 逻辑,但 push 本身并无问题,真正的罪魁祸首是定时器管理失控,导致 randomColor() 被重复、并发执行。
关键问题出在 nextRound() 函数中的定时器调用方式,我们直接看代码:
function nextRound() { let delay = 500 * roundList.length; let repeat = setInterval(randomColor, 500); // ❌ 每次调用都新建一个 interval setTimeout(repeat, delay); // ⚠️ 此处语法错误:setTimeout 第一个参数应为函数,而非 interval ID setTimeout(stop, delay);}
这段代码隐藏着两个致命缺陷:
- 旧定时器未被清理:每次调用 nextRound() 都会生成一个新的 setInterval,而之前的定时器仍在运行,导致 randomColor() 被多个定时器同时触发,数组自然翻倍;
- setTimeout(repeat, delay) 根本无效:repeat 是 setInterval 返回的数值 ID,并非函数,setTimeout 不会执行任何操作。这一行形同虚设,还掩盖了本该执行的清理逻辑。
✅ 正确的修复方法其实非常简单:全局声明一个定时器引用,每次启动新轮次前先清除旧的定时器。修改后的代码如下:
let repeat; // 全局声明,用于存储当前 interval IDfunction nextRound() { clearInterval(repeat); // ✅ 关键:先清除可能存在的旧定时器 const delay = 500 * roundList.length; console.log(`本轮延迟: ${delay}ms`); repeat = setInterval(randomColor, 500); // ✅ 启动新定时器 setTimeout(() => clearInterval(repeat), delay); // ✅ 到时自动停止(替代原 stop 函数)}
另外,建议将 randomColor() 中重复的 roundList.push(expectedInput) 也移除——该操作已在 nextRound() 首次调用时正确执行,保留它只会因定时器叠加导致多次 push。
? 进阶优化提示:
- 将 expectedInput 改为数组索引比对(如 roundList[roundIndex] === actualInput),逻辑更健壮;
- 用 requestAnimationFrame 替代 setTimeout,视觉反馈更精准;
- 为 randomColor() 添加防抖处理,防止用户快速点击导致状态混乱。
修复之后,roundList 将按照轮次线性增长:[green] → [green, red] → [green, red, blue],后续的校验、动画序列和难度递增都能稳定运行。
