//go:build js && wasm package wasmruntime import ( "errors" "fmt" "sync" "syscall/js" ) // Fetch performs an HTTP GET via the browser Fetch API and returns the response // body as a string. It bridges a JS Promise into Go: it registers then/catch // callbacks and blocks the calling goroutine on a channel until the request // settles. // // Because it blocks, call it from a goroutine — never from a reactive callback // or event handler — and write the result into signals when it returns, e.g.: // // go func() { // body, err := Fetch(url) // ... // data.Set(...) // updates the UI reactively // }() func Fetch(url string) (string, error) { var ( once sync.Once done = make(chan struct{}) body string ferr error onResp, onText, onErr js.Func ) finish := func() { once.Do(func() { close(done) }) } onErr = js.FuncOf(func(this js.Value, args []js.Value) any { msg := "fetch error" if len(args) > 0 && args[0].Truthy() { e := args[0] if m := e.Get("message"); m.Truthy() { msg = m.String() } else { msg = e.Call("toString").String() } } ferr = errors.New(msg) finish() return nil }) onText = js.FuncOf(func(this js.Value, args []js.Value) any { if len(args) > 0 { body = args[0].String() } finish() return nil }) onResp = js.FuncOf(func(this js.Value, args []js.Value) any { resp := args[0] if !resp.Get("ok").Bool() { ferr = fmt.Errorf("HTTP %d", resp.Get("status").Int()) finish() return nil } // resp.text() -> Promise resp.Call("text").Call("then", onText).Call("catch", onErr) return nil }) js.Global().Call("fetch", url).Call("then", onResp).Call("catch", onErr) <-done onResp.Release() onText.Release() onErr.Release() return body, ferr }