Using WebSocketStream to write a client

Non-standard: This feature is not standardized. We do not recommend using non-standard features in production, as they have limited browser support, and may change or be removed. However, they can be a suitable alternative in specific cases where no standard option exists.

The WebSocketStream API is a Promise-based alternative to WebSocket for creating and using client-side WebSocket connections. WebSocketStream uses the Streams API to handle receiving and sending messages, meaning that socket connections can take advantage of stream backpressure automatically (no additional action required by the developer), regulating the speed of reading or writing to avoid bottlenecks in the application.

This article explains how to use the WebSocketStream API to create a WebSocket client.

Feature detection

To check whether the WebSocketStream API is supported, you can use the following:

js
if ("WebSocketStream" in self) {
  // WebSocketStream is supported
}

Creating a WebSocketStream object

To create a WebSocket client, you first need to create a new WebSocketStream instance using the WebSocketStream() constructor. In its simplest form, it takes the URL of the WebSocket server as an argument:

js
const wss = new WebSocketStream("wss://example.com/wss");

It can also take an options object containing custom protocols and/or an AbortSignal (see Closing the connection):

js
const controller = new AbortController();
const queueWSS = new WebSocketStream("wss://example.com/queue", {
  protocols: ["amqp", "mqtt"],
  signal: controller.signal,
});

Sending and receiving data

The WebSocketStream instance has an opened property — this returns a promise that fulfills with an object containing a ReadableStream and a WritableStream instance once the WebSocket connection is opened successfully:

js
const { readable, writable } = await wss.opened;

Calling getReader() and getWriter() on these objects provides us with a ReadableStreamDefaultReader and a WritableStreamDefaultWriter respectively, which can be used to read from and write to the socket connection:

js
const reader = readable.getReader();
const writer = writable.getWriter();

To write data to the socket, you can use WritableStreamDefaultWriter.write():

js
writer.write("My message");

To read data from the socket, you can continuously call ReadableStreamDefaultReader.read() until the stream has finished, which is indicated by done being true:

js
while (true) {
  const { value, done } = await reader.read();
  if (done) {
    break;
  }

  // Process value in some way
}

The browser automatically controls the rate at which the client receives and sends data by applying backpressure when needed. If data is arriving faster than the client can read() it, the underlying Streams API exerts backpressure on the server. In addition, write() operations will only proceed if it is safe to do so.

Closing the connection

With WebSocketStream, the information previously available via the WebSocket close and error events is now available via the closed property — this returns a promise that fulfills with an object containing the closing code (see the full list of CloseEvent status codes) and reason indicating why the server closed the connection:

js
const { code, reason } = await wss.closed;

As mentioned earlier, the WebSocket connection can be closed using an AbortController. The necessary AbortSignal is passed to the WebSocketStream constructor during creation, and AbortController.abort() can then be called when required:

js
const controller = new AbortController();
const wss = new WebSocketStream("wss://example.com/wss", {
  signal: controller.signal,
});

// some time later

controller.abort();

Alternatively you can use the WebSocketStream.close() method to close a connection. This is mainly used if you wish to specify a custom code and/or reason:

js
wss.close({
  code: 4000,
  reason: "Night draws to a close",
});

Note: Depending on the server setup and status code you use, the server may choose to ignore a custom code in favor of a valid code that is correct for the closing reason.

A complete sample client

To demonstrate basic usage of WebSocketStream, we've created a sample client. You can see the full listing at the bottom of the article, and follow along with the explanation below.

Note: To get the example working, you'll also need a server component. We wrote our client to work along with the Deno server explained in Writing a WebSocket server in JavaScript (Deno), but any compatible server will do.

The HTML for the demo is as follows. It includes informational

and

elements, a

Now on to the JavaScript. First we grab references to the output

and the close

Full listing

html


  
    
    WebSocketStream Test
  

  
    

WebSocketStream Test

Sends a ping every five seconds