改进从 Promise 中的 WebSocket 获取 onmessage 事件的结果

Improve getting result from onmessage event from WebSocket inside Promise

我有一个 WebSocket 客户端,它发送消息然后接收响应。我希望能够在任何地方调用它并将响应存储回去,如下所示:

    // Global var
    var response

    // Connection to WS
    connect = () => {
        return new Promise(function(resolve, reject) {
            var server = new WebSocket('ws://localhost:8443/test/');
            server.onopen = function() {
                resolve(server);
            };
            server.onerror = function(err) {
                console.log(err)
                reject(err);
            };

        });
    }

    // Send request and receive message back
    async request(data){
        var request = {
            id: "test",
            data: data
        }

        connect().then((server) => {          

            server.send(JSON.stringify(request))

            server.onmessage = ({data}) => {
                response = JSON.parse(data) // save result
            }

        })
        .catch(function(err) {
            console.log(err)
        });
    }

    // Make request
    await request("test")
    
    // Access response
    console.log(response) // prints undefined

最终响应仍未定义,因为当它到达 console.log(response) 时尚未从异步调用中更新。

我能想到的唯一解决方案是像这样添加延迟:

    delay(){
        var promise = new Promise(function(resolve) {
            window.setTimeout(function() {
              resolve('done!');
            }, 10);
          });
          return promise;
    }

    // Make request
    await request("test")

    // Await for delay 
    await delay();
    
    // Access response
    console.log(response) // prints data received!

这是正确的方法吗?添加 10 毫秒的延迟足以让它更新似乎很奇怪。如果您有比我添加的延迟更好的解决方案,请告诉我。

你的 connect() 电话前面 await 不见了。最重要的是,你永远不会告诉你自己等待响应的承诺。这是一个快速修复。

await connect().then((server) => {          

  server.send(JSON.stringify(request))

  return new Promise((resolve, reject) => {
    server.onmessage = ({data}) => {
      response = JSON.parse(data) // save result
      resolve()
    }
  })
})

从技术上讲,您可以将 await 换成 return,因为这是您的结束语,并且您已将 await 置于 request 通话中。

return connect().then((server) => {          

  server.send(JSON.stringify(request))

  return new Promise((resolve, reject) => {
    server.onmessage = ({data}) => {
      response = JSON.parse(data) // save result
      resolve()
    }
  })
})

虽然它有效,但我鼓励您重构连接初始化过程 - 绝对没有理由在每次请求尝试时重新连接到 WS,除非您在它完成时实际断开连接。

https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await