You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Simon游戏灯光闪烁:for循环变量更新快于setInterval中CSS变化的问题

解决Simon游戏中灯光闪烁与音效同步的问题

这个问题我之前做类似记忆类游戏时也碰到过——同步的for循环跑得太快,完全不等闪烁动画和音效完成,导致所有音效一下子全触发,根本对应不上灯光的节奏。核心原因就是同步循环不会等待异步操作(比如setTimeout、音频播放)完成,得用异步控制流来调整时序才行。

第一步:把闪烁函数改成返回Promise

首先得让灯光闪烁的操作变成可等待的异步任务,把原来的闪烁逻辑包装成返回Promise的函数,这样我们就能精准知道闪烁什么时候完成:

// 改造后的闪烁函数,返回Promise
function flashLight(colorElement) {
  // 提前保存原始颜色,方便后续恢复
  if (!colorElement.originalColor) {
    colorElement.originalColor = colorElement.style.backgroundColor;
  }
  
  return new Promise((resolve) => {
    // 切换到高亮颜色
    colorElement.style.backgroundColor = colors[1]; // 假设colors[1]是高亮状态的颜色
    // 等待闪烁时长后恢复原色,标记任务完成
    setTimeout(() => {
      colorElement.style.backgroundColor = colorElement.originalColor;
      resolve();
    }, 500); // 500ms是闪烁持续时间,可根据需求调整
  });
}

第二步:用async/await重构序列播放函数

接下来把播放序列的函数改成异步函数,用for...of循环配合await,让每一次闪烁和音效都等上一个步骤完成后再执行:

// 异步版序列播放函数
async function playSequence(moves) {
  for (const move of moves) {
    // 等待当前灯光闪烁完成
    await flashLight(move.color);
    
    // 检查是否是目标高亮颜色,播放音效
    if (move.color.style.backgroundColor === colors[0]) {
      // 如果音效播放也是异步的,建议也改成返回Promise的形式
      await playSound();
    }
    
    // 可选:在闪烁之间加一段停顿,让节奏更清晰
    await new Promise(resolve => setTimeout(resolve, 200));
  }
}

(可选)让音效播放也支持等待

如果你的音效播放没做异步处理,也可以把它改成返回Promise的函数,避免音效重叠:

function playSound() {
  return new Promise((resolve) => {
    const audio = new Audio('your-highlight-sound.mp3');
    // 音频播放结束后标记任务完成
    audio.addEventListener('ended', resolve);
    audio.play().catch(err => console.error('音效播放失败:', err));
  });
}

为什么这样能解决问题?

await关键字会暂停当前异步函数的执行,直到后面的Promise完成。这样for...of循环就变成了“串行”执行:每一次闪烁完成后,才会检查是否播放音效,等音效(如果有的话)播放完,再等待停顿,然后才处理下一个灯光闪烁。完全不会出现循环跑完了,动画和音效还没完成的情况。

替代方案:递归处理序列

如果你不想用async/await,也可以用递归来逐个处理每个move,本质是利用Promise的链式调用:

function playSequence(moves, currentIndex = 0) {
  // 递归终止条件:所有move处理完毕
  if (currentIndex >= moves.length) return;
  
  const move = moves[currentIndex];
  
  flashLight(move.color)
    .then(() => {
      // 检查是否播放音效
      if (move.color.style.backgroundColor === colors[0]) {
        return playSound();
      }
      return Promise.resolve();
    })
    .then(() => {
      // 等待停顿后处理下一个move
      setTimeout(() => {
        playSequence(moves, currentIndex + 1);
      }, 200);
    });
}

两种方法都能解决问题,个人更推荐async/await的写法,代码更直观易读。

内容的提问来源于stack exchange,提问作者icewizard

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 07:04:33