JavaScript - 在Websocket上使用Promises?

28

我正在使用纯JavaScript中的Websockets,并希望将Promises实现到Websocket函数中。我没有收到任何错误,但Promise不起作用。

使用以下代码,我可以成功连接到套接字服务器,但是Promise似乎被跳过,因为警报的输出始终为“failed”。

有人知道在这种情况下的问题是什么吗? 附注:我在最新的Google Chrome浏览器和最新的Mozilla Firefox浏览器中进行了测试,并且为此示例省略了一些基本的检查/错误处理。

function Connect()
{
    server = new WebSocket('mysite:1234');

    server.onopen = (function()
    {
        return new Promise(function(resolve, reject) 
        {
            if (true)
            {
                resolve();
            }
            else
            {
                reject();
            }
        });
    }).then = (function()
    {
        alert('succeeed');
    }).catch = (function()
    {
        alert('failed');
    });
}

你觉得你有什么能力成功连接呢? - bytesized
1
请注意:您的代码将server.onopen设置为最后一个函数(即警报失败的函数)- 您可以更改.then.catch为任何内容,它仍然会执行相同的操作... 实际上,这与var x =({})。then =({})。catch =({hello:'world'})相同... x将是{hello:'world'},并且具有.then.catch属性的中间对象将被丢弃。 - Jaromanda X
感谢您的解释,肯定很有帮助! - Piet
https://dev59.com/QGAg5IYBdhLWcg3w5eZk#49533457 - Ramil Gilfanov
3个回答

65

您试图在新连接中使用Promise似乎有些误导。您需要从 connect() 返回一个Promise,以便在服务器连接时使用它。

看起来您可能想要这样的代码:

function connect() {
    return new Promise(function(resolve, reject) {
        var server = new WebSocket('ws://mysite:1234');
        server.onopen = function() {
            resolve(server);
        };
        server.onerror = function(err) {
            reject(err);
        };

    });
}

然后,您可以像这样使用它:

connect().then(function(server) {
    // server is ready here
}).catch(function(err) {
    // error here
});

或者像这样使用 async/await

async myMethod() {
  try {
      let server = await connect()
      // ... use server
  } catch (error) {
      console.log("ooops ", error)
  }
}

11
@Piet - 这简单来说并不是 Promise 的工作方式。Promise 是一次性的设备,它们只会触发一次。而输入消息可能会发生多次,因此它们与 Promise 不是一个良好的设计匹配。此外,您不应该在每次发生新事件时创建一个新的 Promise。这根本不是 Promise 的工作方式。当您触发某个异步操作并等待该操作的一次且唯一的响应时,才会创建一个 Promise,并使用该 Promise 监视该结果。对于重复的事件,您将需要使用常规回调函数。 - jfriend00
1
这段代码在某些错误发生时是否存在问题?"onopen"可能只会被调用一次,但"onerror"事件会无限期地附加到websocket上。 - Eric Burel
@EricBurel - 你认为会有什么问题吗?Promises是一次性设备,因此调用reject()超过一次或在调用resolve()之后调用它将忽略后续的调用。 - jfriend00

11

我遇到了完全相同的问题,并创建了一个小型的websocket-as-promised库。它返回连接/断开连接和发送消息的Promise:

const WebSocketAsPromised = require('websocket-as-promised');
const wsp = new WebSocketAsPromised();

// connect
wsp.open('ws://echo.websocket.org')
  .then(() => console.log('Connected.')) 
  // send data and expect response message from server
  .then(() => wsp.sendRequest({foo: 'bar'}))
  .then(response => console.log('Response message received', response))
  // disconnect
  .then(() => wsp.close())
  .then(() => console.log('Disconnected.'));

关于消息,有两种选择:

  1. 如果你发送数据并期望响应消息 - 你可以通过 .sendRequest() 方法使用 promise:

  2. wsp.sendRequest({foo: 'bar'}); // returns promise
    // actually sends message with unique id: {id: 'xxxxx', foo: 'bar'}
    // promise waits response message with the same id: {id: 'xxxxx', response: 'ok'}
    
  3. 如果您只想发送消息而不期望响应,可以使用.send()方法:

  4. wsp.send(data); // returns undefined
    

1
服务器如何以客户端期望的方式响应?另一个发送到套接字?返回? - BHouwens
服务器应该响应一个包含 id 字段的消息。例如,服务器接收到请求 {id: 1, data: 'foo'} 并回复 {id: 1, data: 'bar'} - vitalets

8
为了补充@jfriend00的回答,他的回答试图在每次调用connect方法时创建一个新连接,并且没有考虑连接关闭的情况。
function getSocket() {

  if (getSocket.server && getSocket.server.readyState < 2) {
    console.log("reusing the socket connection [state = " + getSocket.server.readyState + "]: " + getSocket.server.url);
    return Promise.resolve(getSocket.server);
  }

  return new Promise(function (resolve, reject) {

    getSocket.server = new WebSocket(SOCKET_URL);

    getSocket.server.onopen = function () {
      console.log("socket connection is opened [state = " + getSocket.server.readyState + "]: " + getSocket.server.url);
      resolve(getSocket.server);
    };

    getSocket.server.onerror = function (err) {
      console.error("socket connection error : ", err);
      reject(err);
    };
  });
}

并且要使用它

getSocket().then(function(server) {
  // do stuff
}).catch(function(err) {
    // error here
});

或者使用异步/等待方式。
const socket = await getSocket();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接