我用JavaScript (source)编写了Chip-8模拟器,并制作了可播放的浏览器版本here

它包含一个HTML选择:

<select>
  <option value="PONG">PONG</option>
  <option value="TETRIS">TETRIS</option>
</select>


每次选择从文件中加载ROM:

document.querySelector('select').addEventListener('change', event => {
  const rom = event.target.value
  loadRom(rom)
})


loadRom函数获取ROM,将其转换为有用的格式,然后将其加载到CPU类的实例中。 cpu具有fetch-decode-execute周期,该周期被step()调用。我创建了此cycle(主)函数以在setTimeout中调用自身。

const loadRom = async rom => {
  const response = await fetch(`./roms/${rom}`)
  const arrayBuffer = await response.arrayBuffer()
  const uint8View = new Uint8Array(arrayBuffer);
  const romBuffer = new RomBuffer(uint8View)

  cpu.interface.clearDisplay()
  cpu.load(romBuffer)

  let timer = 0
  async function cycle() {
    timer++
    if (timer % 5 === 0) {
      cpu.tick()
      timer = 0
    }

    await cpu.step()

    setTimeout(cycle, 3)
  }

  cycle()
}


正常工作,直到我用select加载新的ROM。现在,循环变得更加复杂,游戏的运行速度提高了一倍。每次加载新的ROM时,它都会再次复合并创建一个新的周期。

我如何创建一个无限循环,但要停止它并开始一个全新的循环而又不增加它?

最佳答案

首先,让当前超时为持久变量,然后在调用clearTimeout之前立即用它调用loadRom。如果尚未加载任何内容,则clearTimeout不会执行任何操作。

但是因为您也有await,所以您需要检查await进行过程中是否加载了新的rom。实现此目的的一种方法是使用另一个持久变量,使用当前的romBuffer-如果它与函数闭包中的romBuffer不同,则另一个rom已启动,因此请立即返回(并且不要递归创建超时)。

let timeout;
let currentRomBuffer;
const loadRom = async rom => {
  const response = await fetch(`./roms/${rom}`)
  const arrayBuffer = await response.arrayBuffer()
  const uint8View = new Uint8Array(arrayBuffer);
  const romBuffer = new RomBuffer(uint8View)
  currentRomBuffer = romBuffer;
  cpu.interface.clearDisplay()
  cpu.load(romBuffer)

  let timer = 0
  async function cycle() {
    timer++
    if (timer % 5 === 0) {
      cpu.tick()
      timer = 0
    }
    await cpu.step();
    if (romBuffer !== currentRomBuffer) {
      return;
    }
    timeout = setTimeout(cycle, 3);
  }
  cycle()
};

关于javascript - 防止运行setTimeout的递归函数复合,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/56845362/

10-11 00:48