DEV Community

Andrea Bertoloni
Andrea Bertoloni

Posted on

Wait for the WebSocket connection to be open, before sending a message

Sometimes when you open a new WebSocket connection and try to send a message, you can receive an Error reporting something like this: Failed to execute 'send' on 'WebSocket': Still in CONNECTING state., because the connection is not open yet.

To avoid that, I saw some people suggesting to send the message on the onopen event; something like:

socket.onopen = () => socket.send(message)

This approach, though, only works if you need to trigger send() once (on open).

A more flexible approach would be to wait for the connection to be open (if it's not already).

The waitForOpenConnection function, in the example below, returns a Promise that resolves when the connection is finally open, checking it every n milliseconds.
If, after a given number of checks, the connection is still not open, the Promise is rejected.
In the sendMessage function, you can see an example of the usage.

const waitForOpenConnection = (socket) => {
    return new Promise((resolve, reject) => {
        const maxNumberOfAttempts = 10
        const intervalTime = 200 //ms

        let currentAttempt = 0
        const interval = setInterval(() => {
            if (currentAttempt > maxNumberOfAttempts - 1) {
                clearInterval(interval)
                reject(new Error('Maximum number of attempts exceeded'))
            } else if (socket.readyState === socket.OPEN) {
                clearInterval(interval)
                resolve()
            }
            currentAttempt++
        }, intervalTime)
    })
}

const sendMessage = async (socket, msg) => {
    if (socket.readyState !== socket.OPEN) {
        try {
            await waitForOpenConnection(socket)
            socket.send(msg)
        } catch (err) { console.error(err) }
    } else {
        socket.send(msg)
    }
}

Link to the GitHub gist

Top comments (6)

Collapse
 
thepeoplesbourgeois profile image
Josh

Nice way of solving the race condition in your code! 👍

With reuse of the socket.send() method being a critical aspect of websocket functionality, it makes sense that you wouldn't want to have it be called in socket.onopen just the one time. Looking at how your code is structured, I wonder if you could avoid (or simplify) the timeout logic by resolving the promise once a true value is seen on checking for the socket to be open?

function waitForOpenSocket(socket) {
  return new Promise((resolve, _reject) => {
    while (socket.readyState !== socket.OPEN) { /* no-op */ }
    return resolve()
  })
}

async function sendMessage(socket, msg) {
  await waitForOpenSocket(socket)
  socket.send(msg)
}
Enter fullscreen mode Exit fullscreen mode

I came into Javascript after async/await had already been standardized, and haven't worked much with the Promise API directly, so I don't actually know whether my while loop gives program execution back to the rest of the app, or makes the app hang until the socket is open... but it should let the app continue working, if I understand promises as much as I think I do.

Collapse
 
fabiwlf profile image
Fabian Wolf • Edited

The while loop stops the code execution in my case.

Better use something like this, works fine for me:

const waitForOpenSocket = (socket) => {
  return new Promise((resolve) => {
    if (socket.readyState !== socket.OPEN) {
      socket.on("open", _ => {
        resolve();
      });
    } else {
      resolve();
    }
  });
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
engelmav profile image
Vincent Engelmann • Edited

This was the best solution for me. Minor tweak, this looks like it's for socketio. The following is for vanilla websockets:


async waitForOpenSocket(socket) {
    return new Promise((resolve) => {
      if (socket.readyState !== socket.OPEN) {
        socket.addEventListener("open", (_) => {
          resolve();
        })
      } else {
        resolve();
      }
    });
  }
Enter fullscreen mode Exit fullscreen mode
Collapse
 
ndrbrt profile image
Andrea Bertoloni • Edited

Well, my point is: since the open state depends on a network request that takes a certain amount of time to be completed and could eventually fail, I want my code to check the status only at given intervals; furthermore, since the request could fail, I don't to want to end up stuck in that loop forever, but eventually break it after n checks.

You could use async/await, but sometimes I just feel more comfortable using a Promise instead (after all an async function is just a sort of a shortcut that returns a Promise)

Collapse
 
mikkorantalainen profile image
Mikko Rantalainen

Why not use socket.addEventListener("open", ...)? That allows adding as many listeners as you wish. Of course, if you must send messages in a predefined order, just put all the send calls in a single function.

Using socket.addEventListener("open", ...) would also allow implementing your wrapper without any polling.

Collapse
 
nathanvg profile image
Nathan VG

Because then your message will only be sent once, when the websocket opens.
When you're using a websocket as an api, as in, you're sending a request and that request can be sent multiple times, you don't want you message to only be sent once on open, you want to be able to send it multiple times.