Go WASM环境下js.FuncOf回调内调用http.Get仍死锁如何解决?
问题成因
- 核心死锁原因:你虽然把
http.Get放到了单独goroutine中执行,但紧接着在js.FuncOf注册的JS回调函数内部用<-done做了阻塞操作。JS本身是单线程运行的,所有异步API(包括http.Get底层依赖的fetch、IndexedDB操作)的回调都需要等待浏览器事件循环空闲时才能执行。而你在onupgradeneeded的回调里阻塞等待通道结果,会直接卡住JS事件循环,导致http.Get的请求结果永远无法返回,通道永远没有数据写入,形成死锁。 - 附加报错原因:从错误日志可以看到主goroutine提前退出,WASM程序主函数如果没有加阻塞逻辑,初始化完成后会直接销毁runtime,后续异步事件触发时就会抛出
Go program has already exited的错误。
解决方案
- 不要在
js.FuncOf注册的回调中执行任何阻塞操作,把HTTP请求、后续写入IndexedDB的逻辑全部放到单独的goroutine中,JS回调执行完直接返回,不要等待异步操作结果。 - 在
main函数末尾添加阻塞逻辑,保证WASM程序不会提前退出。
修正后的代码示例
func (db *DataBase) init(dataSet, table string) { var Ok, Err, Upgrade js.Func db.Request = Window.Get("indexedDB").Call("open", dataSet, 1) Upgrade = js.FuncOf(func(this js.Value, args []js.Value) interface{} { defer Upgrade.Release() db.Data = this.Get("result") fullUrlFile := "https://i.imgur.com/m1UIjW1.jpg" // 所有异步逻辑全部放到goroutine中,回调直接返回不阻塞 go func(fullUrlFile string) { var blob *bytes.Buffer r, e := http.Get(fullUrlFile) if e != nil { panic(e) } defer r.Body.Close() buildFileName() var dest []byte blob = bytes.NewBuffer(dest) io.Copy(blob, r.Body) // 写入IndexedDB的逻辑移到goroutine内部 Store := db.Data.Call("createObjectStore", table, map[string]interface{}{"keyPath": "id"}) Store.Call("add", map[string]interface{}{"id": "00-03", "name": "Karam", "age": 19, "email": "kenny@planet.org", "image": blob}) Window.Call("alert", "First record posted.") }(fullUrlFile) // 直接返回,不做任何阻塞 return nil }) db.Request.Set("onupgradeneeded", Upgrade) } // main函数末尾添加阻塞逻辑,避免程序提前退出 func main() { // 你的原有初始化逻辑 // ... // 永久阻塞,保持WASM进程运行 select {} }
内容的提问来源于stack exchange,提问作者Hasan A Yousef
相关产品推荐
相关产品推荐

