wasm中recover始终返回nil,因panic被映射为js.throw、绕过go的_panic链表,defer+recover在导出函数中无效;必须由js层try...catch捕获或前置校验规避panic。

Wasm模块中直接调用recover会返回nil
Go编译为Wasm时,runtime对panic的处理路径与原生执行完全不同。Wasm目标(GOOS=js GOARCH=wasm)下,panic不会触发传统栈展开,而是被转换为JavaScript端的throw,因此Go层的recover无法捕获——它只作用于Go runtime管理的goroutine panic,而Wasm运行时里没有这个上下文。
defer + recover在Wasm导出函数里无效
即使你在导出给JS调用的函数里写了defer func() { if r := recover(); r != nil { ... } }(),它也永远不会生效。原因有二:
- Wasm模块启动后,Go初始化阶段已结束,所有goroutine(包括
main)早已退出,后续JS触发的Go函数调用是在一个“无goroutine上下文”中执行的 - Go/Wasm的
runtime.panic底层映射到js.throw,绕过了Go的_panic链表机制,gorecover检查gp._panic != nil恒为false
真正有效的防护必须落在JS侧或Go/Wasm桥接层
你需要把防护逻辑移到能实际拦截错误的位置:
- 在JS中用
try...catch包裹对Go导出函数的调用:try { goInstance.exports.myExportedFunc() } catch (e) { console.error("Wasm panic caught in JS:", e) // 可触发重载、上报或降级 } - 若使用
syscall/js注册回调(如js.Global().Set("handleData", js.FuncOf(...))),则在回调内部defer+recover依然无效;必须把可能panic的逻辑包进js.FuncOf的闭包里,并靠JS层兜底 - 对关键计算逻辑做预检:比如避免除零、空指针解引用、切片越界等——这些在Wasm里仍会panic,但可提前用
if拦截,比依赖recover更可靠
别试图在Wasm里模拟“全局recover中间件”
你可能会想写个通用包装器:
func safeCall(f func()) {
defer func() {
if r := recover(); r != nil {
log.Printf("ignored in wasm: %v", r)
}
}()
f()
}
这在Wasm中毫无意义。日志不会输出,recover永远返回nil,且Go的log在Wasm默认不输出到浏览器控制台(需显式重定向)。真正的错误边界只在JS与Wasm交界处,那里没有goroutine,也没有栈回溯——只有throw和catch。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











