WebSocket offers persistent connections and bidirectional communication with a server instead of being limited to HTTPs request/response pattern and strict client-to-server communication. Together with a low per-message overhead, this allows efficient, low-latency communication between the client and the server.

WebSocket connections do not, however, persist across loading of new pages from the same domain, or page reloads. A WebSocket connection only persists as long as the page is still open (not navigated away from).

However, such cross-page persistence would be useful because

it would save on the overhead of establishing a new connection while navigating across pages from a single domain

it would be useful to reduce the number of connections to a server when multiple tabs have opened the same HTML page, each establishing a separate WebSocket connection normally

it might also save employing a mechanism to connect different WebSocket session to a specific user identity on the server, e.g. for chat identity or user tracking

This article discusses shared Web workers, and if and how to use them to solve above issue.

Web workers are one of the newer features or HTML5 and allow JavaScript to be executed in a separate thread from the page main thread that runs the page UI. The main thread and the worker communicate via messages. The existence of a Web worker is directly dependent on that of the page which instantiated it.

Shared Web workers allow the same worker thread to be used by multiple pages from the same domain.

Note: The same origin policy applies to shared Web workers. Means: Web workers can only be "shared" when they come from the same origin.

Shared Web workers are not reliant on any particular page for their existence, as new pages can be added and old ones removed. It is this aspect that inspires the idea that they may possibly persist across page loads - even if there is only a single page from a particular domain open.

So, as mentioned initially, the first possible use case would be to have a WebSocket connection which persists across the loading of new pages from the same domain, e.g. when surfing a Web site.

This would save on all the overhead of creating a new TCP connection, possibly start TLS, do the HTTP upgrade handshake to get the WebSocket connection, possibly establish an application protocol on top of that, and then authenticate the connection and connect it to a user profile on the backend. Depending on the device and connection the user is on, this may result in noticeable performance increases.

Since not only the connection would be held, but JavaScript can also be executed outside of the page, this could then also allow a decoupling of long-running network actions from the page lifecycle. An example of this would be to start the upload of a large file on one page, and allow navigation across the entire site while this continues.

The shared nature also points towards another use case: Sharing a single WebSocket connection across multiple tabs of the same Web site or Web app. Benefits here would be reduction of required server resources (less connections), and the possibility to co-ordinate bandwidth use to the server within the application instead of relying on TCP flow control.

Again, the test is started by opening page_01. Then the second page is loaded in the same tab via the link.

With Opera, this always resulted in the shared Web worker being closed and re-instantiated for the second page. persist was always false, and there was no WebSocket heartbeat.

With Chrome, things were more complicated. When working with just the single tab from the beginning, things were as they were in Opera: No persistence.

Initially that was it, but after a browser update, it was possible to get the shared Web Worker to sometimes persist. An (unreliable) trigger I found was to start with two tabs and then close the tab that was opened second(!). The shared Web worker then persisted across reloads and even, sometimes, when closing the tab and opening a new one!
As my echo server log output showed, new heartbeats continued for minutes.

All in all that's more than discouraging. The desired behavior appeared in a borderline case far removed from real-world usage, was inconsitent, and changed across versions of the same browser.

A look at the spec shows that, as things are, this behaviour won't become more predictable. The main relevant points regarding shared Web worker lifecycle are:

Web workers have a document list where the documents that invoked them are listed.

Once this list is empty (i.e. the last document/tab from which they were invoked) has been closed, implementations are required to set the worker's closing flag to true.

The spec is silent regarding how implementations handle the closing itself.

This means that the setting of the closing flag does not necessarily equate to the worker being closed immediately. If and when this happens is up to the particular browser implementation.

A worker may or may not be closed before it is invoked by a newly loaded page. The closing is more likely to happen than not, but dependent on implementation details and the particulars of the situation at runtime.

The spec does not cover our use case with persistence across page loads in a single tab.

It's clear that there is a need to close orphan workers to recover processing capacity and memory.

The question is then whether there is a simple enough rule that can be added to the spec which would enable the single-tab persistence use case.

The trigger for a special behaviour would be that the next page to be loaded is of the same origin as the present page.

In this case, a (shared) Web worker initiated by the the first page would initially be persisted.

The real question would then be what the conditions for the closing of the worker would be. The newly loaded page might have a delayed instantiation of the worker - or never do so at all.

A simple timeout would, besides the obvious ugliness, not be reliable enough in view of the wide range of possible network and processing delays for the next page, or else wastefully long for the vast majority of use cases.

A canonical event would be needed instead, e.g. if the entire page has loaded, and no inline script has instantiated the same worker script.

I filed an enhancement request regarding this with the W3C WebAppsWG. So in case you have a use case or some other contribution, right there (or on the mailing list) is the place where your comments would have most impact!

Shared Web workers are usable to share a single WebSocket connection across multiple tabs - in those browsers that support them in principle. Browser support will remain problematic for a while, but the present spec covers this use case.

What shared Web workers do not offer is persistent connections across page loads in a single tab. The present spec excludes this use case.

Start experimenting and prototyping for IoT applications with Crossbar.io and Raspberry Pi!

Loaded with all the software you need to make the board part of WAMP applications!

Search this Site

Stay Informed

Crossbar.io and Crossbar.io Fabric

Crossbar.io is an open-source project created and managed by the people behind Crossbar.io Fabric.
Crossbar.io provides the application message routing for real-time Web and IoT applications.
Crossbar.io Fabric will support you in managing your Crossbar.io instances.