Open otaxhu opened 11 months ago
Unrelated to this library, see https://github.com/golang/go/issues/41310
I don't know if you are too busy af but I found the answer and I don't know if you have the time to create an example of a ping pong websocket client compiled in WASM.
So what I did was to require a callback, passing the message as an argument, the reason is because of async nature of (*Conn) Read()
method, minimal reproduction following:
//go:build js && wasm
package main
import (
"context"
"syscall/js"
"nhooyr.io/websocket"
)
func main() {
ctx := context.Background()
conn, _, err := websocket.Dial(ctx, "ws://localhost:8080/ping-pong", nil)
if err != nil {
panic(err)
}
wr, err := conn.Writer(ctx, websocket.MessageText)
if err != nil {
panic(err)
}
js.Global().Set("sendMessageGo", js.FuncOf(func(_ js.Value, args []js.Value) any {
// args[0] is the string that want to sent to the server
wr.Write([]byte(args[0].String()))
wr.Close()
// spawning a goroutine because of conn.Read() is async, and js functions cannot wait
go func() {
_, bb, err := conn.Read(ctx)
if err != nil {
panic(err)
}
// args[1] is the callback, has the shape (bytes) => {/* any operation with the bytes */}
// passing to the callback the bytes in string format
args[1].Invoke(string(bb))
}()
// the js functions must return inmediatly, cannot wait for async operations
return nil
}))
waitCh := make(chan struct{})
<-waitCh
}
Hmm weird. I have this test here which runs in WASM and requires no extra goroutines. https://github.com/nhooyr/websocket/blob/master/ws_js_test.go
I'm def a little busy to look into exactly what's going on in your example. I'll open this up again and look into it later.
don't worry, I saw in the tests that you are not testing the (*Conn) .Read()
method, it's only dialing to a ws server, no reading
No it is, see this line https://github.com/nhooyr/websocket/blob/e3a2d32f704fb06c439e56d2a85334de04b50d32/ws_js_test.go#L32
It writes a message and then confirms the same message is read back.
forget it, you are in fact reading in the wstest.Echo, but I think it has something to do when you are binding the Go function with the JS function
I tried this other example and also worked, with Promise object instead of callback for compatibility with async
and await
js keywords:
//go:build js && wasm
package main
import (
"context"
"syscall/js"
"nhooyr.io/websocket"
)
func main() {
ctx := context.Background()
conn, _, err := websocket.Dial(ctx, "ws://localhost:8080/ws-chat", nil)
if err != nil {
panic(err)
}
js.Global().Set("sendMessageGo", js.FuncOf(func(_ js.Value, args []js.Value) any {
wr, err := conn.Writer(ctx, websocket.MessageText)
if err != nil {
panic(err)
}
wr.Write([]byte(args[0].String()))
wr.Close()
// returning a Promise
return js.Global().Get("Promise").New(js.FuncOf(func(_ js.Value, args []js.Value) any {
// args[0] is resolve callback
// args[1] is reject callback
// spawining a goroutine because of conn.Read() blocking nature
go func() {
_, bb, err := conn.Read(ctx)
if err != nil {
// if there is an error, reject the promise, calling the reject callback
args[1].Invoke(err.Error())
return
}
// if there is no error then pass to resolve the bytes that comes from the server
args[0].Invoke(string(bb))
}()
// also the constructor must return inmediatly, cannot wait for async operations
return nil
}))
}))
waitCh := make(chan struct{})
<-waitCh
}
Ah yes I see, you probably can't block in a JS callback so you have to return a promise. That makes sense. We can document it for sure. See also https://www.reddit.com/r/WebAssembly/comments/nm69e8/blocking_calls_in_wasm/
I'm trying to do the next:
Client Code:
index.html
:Server code:
Also I'm going to attach my Makefile so you can see the commands when I'm compiling:
Structure of
dist
folder:But i'm getting the following errors after calling
window.goWS("ws://localhost:8080/ws-chat")
from the firefox browser's console:Console: