Sending and Receiving
Once a connection is OPEN, the day-to-day work is just two things: pushing messages out with send() and reacting to messages coming in through the message event. This lesson nails down both directions, plus the two rules that trip people up most — when you may send, and what order things arrive.
Sending with ws.send()
Section titled “Sending with ws.send()”To send, you call send() with your payload:
ws.send('hello, server');ws.send(JSON.stringify({ type: 'chat', text: 'hi' }));send() accepts a string or binary data (we cover binary in the next lesson). It does not return a value and gives you no acknowledgement — it simply hands the message to the connection. A common pattern is to send structured data by serializing an object to JSON, then parsing it on the other end.
Receiving via the message event
Section titled “Receiving via the message event”Incoming messages arrive as message events, and the payload is always on event.data:
ws.onmessage = (event) => { const data = event.data; // a string (or binary, next lesson) const msg = JSON.parse(data); // if the peer sent JSON console.log(msg.type, msg.text);};event.data is the only place the payload lives. If you sent JSON, you parse it here; if you sent plain text, you use it directly. The handler runs once per message and may fire as often as the server has things to say.
You can only send when OPEN
Section titled “You can only send when OPEN”This is the single rule worth tattooing on your memory: send() is only valid while readyState is OPEN.
- Call
send()while stillCONNECTINGand it throws anInvalidStateError— the connection is not ready. - Call
send()afterCLOSINGorCLOSEDand the data is silently dropped; it never reaches the peer.
So the safe pattern is to send from inside onopen, or to guard every call:
function safeSend(ws: WebSocket, data: string) { if (ws.readyState === WebSocket.OPEN) { ws.send(data); } else { console.warn('not open — dropping or queuing message'); }}Ordering is guaranteed
Section titled “Ordering is guaranteed”A WebSocket runs over a single TCP connection, and that buys you a strong, simple guarantee: messages are delivered in the order you sent them. If you call send('a') then send('b') then send('c'), the peer receives a, b, c — never reordered, never duplicated. The same holds for messages the server sends to you.
sequenceDiagram
participant App as Your code
participant WS as WebSocket
participant Srv as Server
Note over WS: readyState = OPEN
App->>WS: send("a")
App->>WS: send("b")
WS->>Srv: a
WS->>Srv: b
Srv-->>WS: reply to a
WS-->>App: message event (data = reply to a)
Srv-->>WS: reply to b
WS-->>App: message event (data = reply to b) What ordering does not promise is a request/response pairing. A WebSocket has no built-in notion of “this reply belongs to that send” — if you need that, you add your own correlation id inside the message. Ordering, yes; matching, that is your job.
Send and receive, live
Section titled “Send and receive, live”The demo uses an in-page echo socket with the real WebSocket API, so it runs anywhere with no server. It sends three messages in a row from inside onopen and prints each echo as it returns — in order. In a real app only the first line would change to new WebSocket('wss://your-server').
Notice the first attempt — too early — is blocked because the socket is still CONNECTING. Then, once open, the three numbered messages come back as 1, 2, 3, in exactly the order they were sent. That is send(), the message event, the OPEN-only rule, and ordering, all in one run.