纹理加载错误需通过Promise封装+img.crossOrigin声明+naturalWidth校验来主动捕获,而非依赖onerror;生成器throw仅作协程内错误注入,真正兜底靠可控Promise链与GPU层gl.getError()双重防御。

直接用生成器的 throw 方法来兜底纹理加载错误,不是常规做法——生成器本身不处理异步异常,它只是暂停/恢复执行的协程机制。真正起作用的是配合 try…catch、yield 和 Promise 状态流转,把错误“抛回”生成器内部再捕获。Canvas 游戏引擎中纹理加载失败常表现为静默黑屏、白图或 SecurityError,靠裸 onerror 往往捕获不到,必须从加载链路源头做可控拦截。
纹理加载必须走可控 Promise 链
Image 对象的 onerror 在跨域未声明时根本不会触发,浏览器直接静默失败。所以不能依赖事件回调,而要封装成 Promise,并显式控制 reject 时机:
- 创建 Image 实例后立即设置
img.crossOrigin = 'anonymous',否则后续 canvas 绘制可能因污染报SecurityError - 手动检查
img.naturalWidth > 0,避免 onload 触发但实际是空图(常见于 404 返回空白页) - 给 src 加时间戳参数(如
?t=+Date.now())防止缓存干扰错误复现
用生成器模拟协程并注入错误处理点
生成器函数本身不执行异步操作,但它能暂停在 yield 处,等外部代码 resolve/reject 后再继续。关键在于:Promise 拒绝时,调用生成器的 throw() 方法,让错误进入生成器内部的 try…catch 块:
- 定义生成器函数,内部用
try…catch包裹yield loadTexture(...) - 外部执行时,拿到迭代器后调用
next()启动;Promise 拒绝时,调用it.throw(err) - 这样错误就“回到”生成器作用域,可统一降级(如加载占位图)、记录日志、触发重试逻辑
结合资源管理器做自动恢复
单次 throw 只解决当前纹理,但游戏需持续运行。建议在资源管理器层封装带重试的加载器,并与生成器协同:
- 加载器内部用指数退避重试(如 1s、2s、4s),失败后 emit
'error'事件 - 生成器监听该事件,收到后调用自身
throw(),进入错误处理分支 - 错误分支里可切换为低清替代纹理、隐藏相关对象、或向 UI 发送加载失败提示
避免 GPU 驱动层错误干扰主线程
纹理成功解码但上传 GPU 失败(比如 WebGL context 丢失、显存不足),这类错误无法被 JS 层 Promise 捕获。此时需补充防御:
- 每次调用
gl.texImage2D后立即检查gl.getError(),非 0 值说明上传失败 - 若检测到
GL_OUT_OF_MEMORY或GL_INVALID_VALUE,主动清理已分配纹理,触发重新加载流程 - 将该检查逻辑封装进纹理上传函数,使其成为生成器 yield 的一部分,保证错误可被 throw 捕获



















