对象销毁后其方法内的setTimeout仍持续运行问题求助
解决太空侵略者游戏中子弹销毁后setTimeout循环持续运行的问题
问题根源
你当前的fire方法是无限递归调用setTimeout,即便子弹被从bullets对象中删除、设置为null,已经被调度的setTimeout仍会执行,且每次执行又会重新调度下一次,导致循环无法终止。另外代码里的bullet.destroy是属性访问而非方法调用,根本没执行销毁逻辑。
优化方案
1. 给Bullet类添加活跃状态标记
通过isActive状态控制fire循环的终止,在每次调度前检查状态,不活跃则停止递归。
2. 用requestAnimationFrame替代setTimeout
这是浏览器专门为动画场景设计的API,会根据屏幕刷新率自动调整执行时机,动画更流畅且性能更优。
3. 完善销毁逻辑
确保销毁时标记子弹为不活跃,同时清理DOM元素(如果需要)。
修改后的代码
Bullet类部分
class Bullet { constructor(name) { this.name = name; this.isActive = true; // 标记子弹是否处于活跃状态 this.bulletmove = new CustomEvent("bulletmove", { detail: this.name }); // 初始化DOM元素等其他代码... } /** * 控制子弹向上移动的循环逻辑 */ fire() { // 子弹已不活跃,直接终止循环 if (!this.isActive) return; this.moveUp(); window.dispatchEvent(this.bulletmove); // 使用requestAnimationFrame调度下一帧动画 requestAnimationFrame(() => this.fire()); // 如果坚持用setTimeout,同样要加状态判断: // setTimeout(() => this.fire(), 50); } /** * 销毁子弹的方法 */ destroy() { this.isActive = false; // 标记为不活跃,终止fire循环 // 移除DOM元素(假设你的子弹DOM存在this.element属性) if (this.element) { this.element.remove(); } } }
控制器部分
window.addEventListener('bulletmove', checkCollision, false); /** * 检测子弹是否出界并销毁 */ function checkCollision(e) { const bulletName = e.detail; const bullet = bullets[bulletName]; // 子弹已被删除,直接返回 if (!bullet) return; if (bullet.bottom < 0) { bullet.destroy(); // 调用销毁方法 delete bullets[bulletName]; } }
关键说明
- 状态控制循环:
isActive标记是终止递归的核心,只要在销毁时设置为false,下一次fire执行就会直接返回,不会继续调度下一轮。 - requestAnimationFrame优势:相比setTimeout,它能避免丢帧、减少不必要的执行,尤其在游戏这种高频动画场景下体验更好。
- 避免无效操作:在checkCollision里先判断bullet是否存在,防止子弹已被删除后出现undefined报错。
内容的提问来源于stack exchange,提问作者Ben
相关产品推荐
相关产品推荐

