登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go问答

JavaScript 异步回调函数无法实现 WebSocket 非阻塞行为

来源:stackoverflow

时间:2024-03-06 23:30:27 201浏览 收藏

在Golang实战开发的过程中,我们经常会遇到一些这样那样的问题,然后要卡好半天,等问题解决了才发现原来一些细节知识点还是没有掌握好。今天golang学习网就整理分享《JavaScript 异步回调函数无法实现 WebSocket 非阻塞行为》,聊聊,希望可以帮助到正在努力赚钱的你。

问题内容

我正在运行两个用 go 编写的进程,其中一个进程 (sender.go) 向另一个进程 (listener.go) 发送消息,同时通过 websockets 陷入 for 循环。

问题是listener.go只有在终止循环后才意识到它收到了消息。

我已经尝试了几个 websocket 库,我什至尝试使用常规的 tcp 流,但在 webassembly 中编译时它不会工作,因为浏览器不支持它。尽管有这种行为,syscall/js websocket 似乎是完美的选择。

这里是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
}

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  ")
}

如果有人愿意重现这个问题,这里是 websocket 服务器,它接收来自发送者的消息并将其转发给接收者,用 node.js 编写,只需复制并粘贴它,它可以在多个项目中正常工作

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)
        }
      }
    });
  });
});

我期望listener.go在退出for循环之前接收消息

ps:使用 sleep 语句并不是一个好的帮助,因为当这个 for 循环在 js 回调中运行时,sleep 语句会输出一个panic。


解决方案


首先,请注意,如果您生成 wasm Go 代码,它不是多线程的,至少在今天(2019 年 9 月)不是。看 How to implement multithreading in wasm created using golang? 所以只有一个实际执行线程。

然后,请记住,goroutine 可以在任何可用线程上协作执行多任务。由于只有一个线程,因此任何时候都只有一个 goroutine 在运行。如果你正在运行的 goroutine 没有将处理器交给其他 goroutine,那么其他 goroutine 将不会运行。

你可以隐式放弃处理器——例如,通过等待通道或互斥体——或者显式地,通过 time.Sleep 或 runtime.Gosched。如果没有这样的调用,任何注意到传入消息的 goroutine 永远没有机会看到它和你的回调(将在这个或某个第三个 goroutine 中运行)永远不会被调用。

(如果您在本机运行,而不是在 wasm 中运行,您通常会获得更多线程,并且能够让其他 CPU 运行其他任务,即使一个 CPU 处于紧密循环中。)

以上就是《JavaScript 异步回调函数无法实现 WebSocket 非阻塞行为》的详细内容,更多关于的资料请关注golang学习网公众号!

声明:本文转载于:stackoverflow 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>