syscall / js websocket回调在非阻塞行为中不响应

I have two processes running, written in Go in which one (sender.go) sends a message to another (listener.go) while being stuck in a for loop via websockets.

The issue is that listener.go only realizes it received the message after it terminates the loop.

I have tried several websocket libraries, I even tried using regular tcp streams, but it won't work when compiled in webassembly because browser won't support it. The syscall/js websocket seemed to be the perfect fit despite this behaviour.

here's listener.go

func registerCallbacks(ws js.Value) {
    ws.Call("addEventListener", "message", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
        message := args[0].Get("data").String()
        fmt.Println("message received ")
        fmt.Println(message)
        return nil
    }))
}


func main() {
    c := make(chan struct{}, 0)
    toIP := "127.0.0.1"
    toPort := 8081
    ws := js.Global().Get("WebSocket").New(fmt.Sprintf("ws://%s:%d/ws", toIP, toPort))
    registerCallbacks(ws)
    const bigNum uint64 = 100000 * 10000
    cb := func(this js.Value, args []js.Value) interface{} {
      for i := uint64(0); i < bigNum; i++ {
        doThings()
        if i%10000000 == 0 {
            fmt.Println("skimmed ten millions ")
        }
      }
      fmt.Println("Exited for loop !!")
      return nil
    }
    // call cb() in script of index.html from a button
    js.Global().Set("cb", js.FuncOf(cb))
    <-c
}

So is sender.go,

func main() {
    toIP := "127.0.0.1"
    toPort := 8081
    ws := js.Global().Get("WebSocket").New(fmt.Sprintf("ws://%s:%d/ws", toIP, toPort))
    time.Sleep(1 * time.Second)
    fmt.Println("sending  ")
    ws.Call("send", js.ValueOf("msg"))
    fmt.Println("Program exit  ")
}

If someone's willing to reproduce the issue here's the websocket server which receives message from sender to forward it to receiver, written in node.js, just copy and paste this, it works fine with several projects

const port = 8081;
const host = '127.0.0.1';

var WebSocketServer = require('websocket').server;
var http = require('http');

var server = http.createServer(function(request, response) {});

server.listen(port, host, () => {
    console.log('WS Server is running on port ' + port + '.');
});

wsServer = new WebSocketServer({
    httpServer: server
});

let sockets = [];


wsServer.on('request', function(request) {
  var connection = request.accept(null, request.origin);
  sockets.push(connection)
  console.log("Connection with node initiated")
  connection.on('message', function(message) {
    console.log("message received "+message)
    sockets.forEach(function(s, index, array) {
      if (message.type === 'utf8') {
        broadcastData = message.utf8Data
        console.log("message is: "+ broadcastData)
        if(s!= connection) {
          console.log('send data to ' + s.socket.remotePort + ': ' + broadcastData);
          s.sendUTF(broadcastData)
        }
      }
    });
  });
});

I am expecting listener.go to receive message before it exits the for loop

PS: Using sleep statement is not a good help , because when this for loop runs inside a js callback, the sleep statement outputs a panic.

First, note that if you are generating wasm Go code, it's not multi-threaded, at least not today (September 2019). See How to implement multithreading in wasm created using golang? So there's only one actual execution thread.

Then, remember that goroutines are cooperatively multi-tasked onto whatever threads are available. Since there's only one thread, there's only one goroutine running at any time. If your running goroutine does not give up the processor to other goroutines, those other goroutines won't run.

You can give up the processor implicitly—for instance, by waiting on a channel or a mutex—or explictly, via time.Sleep or runtime.Gosched. Without such a call, any goroutine that would notice the incoming message never gets a chance to see it, and your callback—which will run in this or some third goroutine—never gets called.

(If you run natively, rather than in wasm, you will generally get more threads and will be able to have other CPUs run other tasks even while one is in a tight loop.)