go编译的wasm需js.global().set()导出函数、go.js胶水脚本和阻塞机制三者配合;因go无原生导出语法,必须手动挂载到全局对象,且js.funcof需严格签名、注意类型转换、panic处理及并发安全,main不阻塞则实例立即退出。

Go 编译出的 WASM 不能直接在浏览器里跑起来,必须靠 js.Global() 暴露函数 + go.js 胶水脚本 + 正确的阻塞机制三者配合,缺一不可。
为什么 js.Global().Set() 是唯一导出函数的方式
Go 没有类似 JavaScript 的 export 或 Rust 的 #[wasm_bindgen] 语法,所有从 Go 到 JS 的可调用入口都得手动挂到全局对象上。不调用 js.Global().Set("name", ...),JS 就根本看不到这个函数。
-
js.FuncOf()必须包装一个符合func(js.Value, []js.Value) interface{}签名的函数,不能直接传普通 Go 函数 - 返回值会被自动转成 JS 类型:Go 的
int、string、bool都行,但struct或未导出字段会变成undefined - 如果函数 panic,WASM 实例会直接退出,JS 侧收不到错误,只能靠
defer/recover包一层再返回错误信息 - 别在
js.FuncOf闭包里捕获外部变量并修改——它可能被多次并发调用,而 Go 的 goroutine 模型在 wasm 里是模拟的,没有真正的线程安全保证
main() 里不阻塞就会秒退
浏览器中没有“进程生命周期”概念,Go 主 goroutine 一结束,整个 wasm 实例就销毁。常见写法是 select {} 或 ,但要注意:
-
select {}最简洁,但无法响应外部信号(比如 JS 主动通知退出) -
js.Wait()是官方推荐方式(Go 1.21+),它内部监听 JS 事件循环,比纯select更友好 - 千万别用
time.Sleep(time.Hour)—— 它在 wasm 里不触发调度,等于白等 - 如果用了
http.ListenAndServe或其他阻塞服务,它在 wasm 里根本不可用,会 panic
go.js 不是可选配件,版本错配必报错
go.js 在 $(GOROOT)/misc/wasm/go.js,它不是加载器,而是运行时核心:管理内存堆、模拟 goroutine、桥接 JS 异步回调。混用不同 Go 版本的 go.js 和 .wasm 文件,大概率看到这类错误:
runtime: failed to create new OS thread undefined symbol: runtime.wasmExit TypeError: Cannot read property 'memory' of undefined
- 每次升级 Go,务必用对应版本的
go.js,不要缓存旧版 - 本地开发别用
file://协议打开 HTML——instantiateStreaming要求服务器返回Content-Type: application/wasm,可用python3 -m http.server 8080或goexec "http.ListenAndServe(`:8080`, http.FileServer(http.Dir(`.`)))" - 如果要用自定义加载逻辑(比如带 fallback 的 wasm 加载),必须完整复现
go.js里的importObject结构,漏掉env或go命名空间里的任意一个函数都会卡住
JS 调用 Go 函数时参数和返回值怎么转
syscall/js 的类型转换是隐式的,但边界很窄。比如:
-
args[0].Int()遇到null、undefined或非数字字符串会返回 0,不报错;要用args[0].Type() == js.TypeString先判断类型 -
js.ValueOf(map[string]interface{})会变成 JS 的空对象{},因为 map 不支持直接序列化;得手动转成js.Object或用json.Marshal+JSON.parse - 想把 JS 的
Promise传给 Go?不行。syscall/js不识别 Promise,只能传then回调函数,由 Go 手动调用.Call("then") - JS 调 Go 函数后立刻读返回值没问题,但若 Go 函数内部启动 goroutine 异步执行,返回值必须是同步结果——wasm 是单线程模型,没有真正的异步返回通道
最易被忽略的一点:js.Global().Set() 绑定的函数,JS 侧调用时 this 是 window,不是某个实例对象;如果需要上下文绑定,得在 JS 层用 .bind() 或箭头函数包装,Go 侧无法控制。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











