防止递归函数运行 setTimeout复合
Prevent recursive function running setTimeout from compounding
我在 JavaScript (source) and made a playable browser version here 中写了一个 Chip-8 仿真器。
它包含一个 HTML select:
<select>
<option value="PONG">PONG</option>
<option value="TETRIS">TETRIS</option>
</select>
每次 selected:
从文件加载 ROM
document.querySelector('select').addEventListener('change', event => {
const rom = event.target.value
loadRom(rom)
})
此 loadRom
函数获取 ROM,将其转换为有用的形式,并将其加载到 CPU class 的实例中。 cpu
有一个使用 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 时,它都会再次复合并创建一个新的循环。
如何创建一个无限循环,然后停止它并开始一个全新的循环而不复合它?
首先,将当前超时设置为持久变量,然后在调用 loadRom
之前用它调用 clearTimeout
。如果尚未加载任何内容,clearTimeout
将不会执行任何操作。
但是因为您也有 await
s,所以您需要在 await
s 进行时检查是否加载了新的 rom。实现这一点的一种方法是使用另一个持久变量,当前使用的 romBuffer
- 如果它与函数闭包中的 romBuffer
不同,那么另一个 rom 已经启动,所以 return 立即(并且不要递归地创建超时)。
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()
};
尝试使用 setInerval
并跟踪句柄。
然后在加载新的(或相同的)rom 时清除它。
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)
// if rom is loaded, clear it!
if(this.lastLoad)
clearInterval(this.lastLoad);
let timer = 0
async function cycle() {
timer++
if (timer % 5 === 0) cpu.tick()
await cpu.step()
}
// keep track the handle.
this.lastLoad = setInterval(cycle, 3);
}
我在 JavaScript (source) and made a playable browser version here 中写了一个 Chip-8 仿真器。
它包含一个 HTML select:
<select>
<option value="PONG">PONG</option>
<option value="TETRIS">TETRIS</option>
</select>
每次 selected:
从文件加载 ROMdocument.querySelector('select').addEventListener('change', event => {
const rom = event.target.value
loadRom(rom)
})
此 loadRom
函数获取 ROM,将其转换为有用的形式,并将其加载到 CPU class 的实例中。 cpu
有一个使用 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 时,它都会再次复合并创建一个新的循环。
如何创建一个无限循环,然后停止它并开始一个全新的循环而不复合它?
首先,将当前超时设置为持久变量,然后在调用 loadRom
之前用它调用 clearTimeout
。如果尚未加载任何内容,clearTimeout
将不会执行任何操作。
但是因为您也有 await
s,所以您需要在 await
s 进行时检查是否加载了新的 rom。实现这一点的一种方法是使用另一个持久变量,当前使用的 romBuffer
- 如果它与函数闭包中的 romBuffer
不同,那么另一个 rom 已经启动,所以 return 立即(并且不要递归地创建超时)。
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()
};
尝试使用 setInerval
并跟踪句柄。
然后在加载新的(或相同的)rom 时清除它。
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)
// if rom is loaded, clear it!
if(this.lastLoad)
clearInterval(this.lastLoad);
let timer = 0
async function cycle() {
timer++
if (timer % 5 === 0) cpu.tick()
await cpu.step()
}
// keep track the handle.
this.lastLoad = setInterval(cycle, 3);
}