28

I am using Websockets in pure Javascript and I want to implement Promises into the Websocket functions. I don't get any errors but the Promise doesn't work.

Using the following code I can connect succesful to the socketserver but the Promise seems to be skipped because the output of the alert is always "failed".

Does somebody knows what the problem is in this case? Ps: I did the tests in the latest Google Chrome browser and the latest Mozilla Firefox browser, and I left out some basic checking/error handling for this example.

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');
    });
}
Piet
  • 2,188
  • 5
  • 19
  • 30
  • What makes you think you can connect successfully? – bytesized Feb 17 '17 at 19:34
  • 1
    FYI: Your code will set `server.onopen` to the last function (the one that alerts failed) - you could change `.then` and `.catch` to anything and it would still do the same ... effectively it's the same as `var x = ({}).then = ({}).catch = ({hello:'world'})` ... x will be `{hello: 'world'}` and the intermediate objects with `.then` and `.catch` properties are discarded – Jaromanda X Feb 17 '17 at 22:05
  • Thanks for the explanation, helpful for sure! – Piet Feb 19 '17 at 20:07
  • https://stackoverflow.com/a/49533457/1359764 – Ramil Gilfanov Mar 29 '18 at 03:45

3 Answers3

64

Your attempt to use promises with the new connection seems a bit misguided. You will want to return a promise from connect() so you can use it to know when the server is connected.

It seems like you probably want something like this:

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

    });
}

Then, you would use it like this:

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

or with async/await like this:

async myMethod() {
  try {
      let server = await connect()
      // ... use server
  } catch (error) {
      console.log("ooops ", error)
  }
}
husayt
  • 14,553
  • 8
  • 53
  • 81
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • 11
    @Piet - That simply isn't how promises work. Promises are one-shot devices. They only ever fire once. Incoming messages can happen more than once so they are not a good design match for promises. Plus, you don't create a new promise every time a new event occurs. That isn't how promises work at all. You create a promise when you trigger some async operation and you are waiting for the one and only one response from that operation. The promise is then used to monitor that one result. You will just need to use regular callbacks for recurring events. – jfriend00 Feb 19 '17 at 20:28
  • 1
    Doesn't this code have issue when certain error happens? The "onopen" might be called only once, but the "onerror" event stays attached to the websocket indefinitely. – Eric Burel Oct 17 '22 at 15:05
  • @EricBurel - What issue do you think there would be? Promises are one-shot devices so calling `reject()` more than once or calling it after `resolve()` has been called will just ignore the subsequent calls. – jfriend00 Oct 17 '22 at 21:40
10

I faced exactly the same problem and created tiny websocket-as-promised library. It returns promises for connecting / disconnecting and sending messages:

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.'));

Concerning messages there are two options:

  1. if you send data and expect response message - you can use promise via .sendRequest() method:

    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'}
    
  2. if you just want to send message and do not expect response - use .send() method:

    wsp.send(data); // returns undefined
    
vitalets
  • 4,675
  • 1
  • 35
  • 35
  • 1
    How does the server respond to this in a manner that the client expects? Another send to the socket? A return? – BHouwens Apr 07 '18 at 18:38
  • Server should respond with a message, containing the `id` field. For example, server receives request `{id: 1, data: 'foo'}` and respond with `{id: 1, data: 'bar'}`. – vitalets Apr 09 '18 at 11:09
8

To extend @jfriend00 answer, as his answer tries to create a new connection every time the connect method is called and it does not consider the case where the connection is closed

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

and to use it

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

or with async/await

const socket = await getSocket();
Hyyan Abo Fakher
  • 3,497
  • 3
  • 21
  • 35