-
Notifications
You must be signed in to change notification settings - Fork 22.9k
New WebSockets client guide #40931
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New WebSockets client guide #40931
Conversation
seems not a single file was built! 🙀 (comment last updated: 2025-09-09 15:02:02) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work @wbamberg; this is a massive improvement over what we had before, in terms of both demo and guide. I've left you a few comments, but nothing major really.
I would still like to see a tech review from the other folks on the thread.
files/en-us/web/api/websockets_api/writing_websocket_client_applications/index.md
Outdated
Show resolved
Hide resolved
files/en-us/web/api/websockets_api/writing_websocket_client_applications/index.md
Outdated
Show resolved
Hide resolved
files/en-us/web/api/websockets_api/writing_websocket_client_applications/index.md
Outdated
Show resolved
Hide resolved
If you don't specify a protocol string, an empty string is assumed. | ||
The `WebSocket` constructor takes one mandatory argument, which is the URL of the WebSocket server to connect to. In this case, since we're running the server locally, we're using the address of localhost. | ||
|
||
The constructor takes another optional argument [`protocols`](/en-US/docs/Web/API/WebSocket/WebSocket#protocols), which allows a single server to implement multiple sub-protocols. We're not using this feature in our example. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with your note that this probably should be discussed in a separate guide. It certainly doesn't belong in a basic usage guide, although from reading this and following the link, it feels like it'd be nicer if we provided some more info about it somewhere on MDN.
files/en-us/web/api/websockets_api/writing_websocket_client_applications/index.md
Outdated
Show resolved
Hide resolved
files/en-us/web/api/websockets_api/writing_websocket_client_applications/index.md
Outdated
Show resolved
Hide resolved
files/en-us/web/api/websockets_api/writing_websocket_client_applications/index.md
Outdated
Show resolved
Hide resolved
|
||
The back/forward cache, or {{glossary("bfcache")}}, enables much faster back and forward navigation between pages that the user has recently visited. It does this by storing a complete snapshot of the page, including the JavaScript heap. | ||
|
||
The browser pauses and then resumes JavaScript execution when a page is added to or loaded from the bfcache. This means that, depending on what the page is doing, it's not always safe for the browser to use the bfcache for the page. If the browser determines that it is not safe, then the page will not be added to the bfcache, and then the user will not get the performance benefit that it can bring. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The browser pauses and then resumes JavaScript execution when a page is added to or loaded from the bfcache. This means that, depending on what the page is doing, it's not always safe for the browser to use the bfcache for the page. If the browser determines that it is not safe, then the page will not be added to the bfcache, and then the user will not get the performance benefit that it can bring. | |
The browser pauses and then resumes JavaScript execution when a page is added to or loaded from the bfcache. This means that, depending on what the page is doing, it's not always safe for the browser to use the bfcache for the page. If the browser determines that it is not safe, the page will not be added to the bfcache, and the user will not get the performance benefit that it can bring. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, maybe link to https://developer.mozilla.org/en-US/docs/Web/API/Performance_API/Monitoring_bfcache_blocking_reasons for more information on why pages might not be added to the bfcache.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder why you are deleting my "then"s? I like my thens. "If....then" I think is clearer structurally than an implicit then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's a shame that this is presented as a page on how to use a Chrome-only feature. It would be better to have a page under https://developer.mozilla.org/en-US/docs/Web/Performance, that talks about the bfcache as it works across browsers, and the various reasons pages might not be bfcache-friendly in various browsers, and links to notRestoredReasons
as a tool you can use in that context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would say "restored from the bfcache" rather than "loaded from the bfcache".
The bfcache isn't a "cache" really. More a preservation and restore of a page's state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's a shame that this is presented as a page on how to use a Chrome-only feature. It would be better to have a page under https://developer.mozilla.org/en-US/docs/Web/Performance, that talks about the bfcache as it works across browsers, and the various reasons pages might not be bfcache-friendly in various browsers, and links to notRestoredReasons as a tool you can use in that context.
That's fair. The concepts are generally the same. It's just that only Chrome currently exposes the reasons in an easy manner. I think testing each in each browser is gonna be quite painful without that as could run into false positives (e.g. you think it didn't use bfcache for X reasons, but it was actually for Y reason).
Either way I'm in two minds whether you should link it. I mean it is the only definitive list (and is part of the HTML spec btw!), but also is only queryable from Chrome as you say.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, sorry, I didn't mean to sound so negative about it! It is a good article and obviously a useful API. And we should link to it. I just think it would be great to have a browser-agnostic article about bfcache compatibility on MDN, that's not tied to specific APIs. But I'm not planning to write that any time soon.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
}); | ||
``` | ||
|
||
Conversely, by listening for the {{domxref("Window.pageshow_event", "pageshow")}} event, you can seamlessly start the connection again when the page is loaded, or when it is retrieved from the bfcache. So in our example we will add all the code to initialize our WebSocket and set up its event listeners inside the `pageshow` event handler: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Conversely, by listening for the {{domxref("Window.pageshow_event", "pageshow")}} event, you can seamlessly start the connection again when the page is loaded, or when it is retrieved from the bfcache. So in our example we will add all the code to initialize our WebSocket and set up its event listeners inside the `pageshow` event handler: | |
Conversely, by listening for the {{domxref("Window.pageshow_event", "pageshow")}} event, you can seamlessly start the connection again when the page is loaded or retrieved from the bfcache. In our example, we add all the code to initialize our WebSocket and set up its event listeners inside the `pageshow` event handler: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again I think I like my extra words around ", or when it is". I think it makes the structure clearer. I suppose this is subjective?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pageshow
is also fired on page load. So might wanna mention that (to avoid people opening a connection twice on pageload).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again I think I like my extra words around ", or when it is". I think it makes the structure clearer. I suppose this is subjective?
Yes, I think so. They seem a little wordy to me, but it is not wrong. Up to you whether you want to commit these suggestions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pageshow is also fired on page load. So might wanna mention that (to avoid people opening a connection twice on pageload).
I do mention this:
you can seamlessly start the connection again when the page is loaded or retrieved from the bfcache.
good enough?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mmm.... it's the "start the connection again" which is not quite working for me. If you're using this as the main way of initialising the web socket on initial page load, then it't not again. Also "when the page is loaded or retrieved from the bfcache" could easily be misread to mean "when the page is (loaded or retrieved) from the bfcache" rather than "when the page (is loaded) or (retrieved from the bfcache)"
WDYT about something more explicit like this?:
Conversely, by listening for the {{domxref("Window.pageshow_event", "pageshow")}} event, you can seamlessly start the connection again when the page is retrieved from the bfcache. Since the
pageshow
event also fires on page load, this can also be used to initiate the websocket connection on initial page load."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah, makes sense, I missed the "again". Does f15529d work for you?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Thanks.
``` | ||
|
||
It may be helpful to examine the socket's {{domxref("WebSocket.bufferedAmount", "bufferedAmount")}} attribute before attempting to close the connection to determine if any data has yet to be transmitted on the network. | ||
If this value isn't 0, there's pending data still, so you may wish to wait before closing the connection. | ||
If you run our example, try navigating to a different page, then back to the example. In Chrome, you should see that the example starts the connection again, and keeps its original context: so, for example, it remembers the count of exchanged messages. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In Firefox and Safari, it just seems to start the app again from its initial state, which is a shame. Worth mentioning?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not what I see in Safari, and I would like to know why we see different things here.
But it is what I see in Firefox, and that's expected: because the page isn't added to the bfcache, then it is reloaded, so the log starts out empty and the counter starts again at zero.
In Safari, after a forward/back navigation, I see this:
Here, I clicked "forward" after exchange 4, and then "back". I think what happens here is that:
- the page was added to/loaded from the bfcache, so the HTML/JS is not reloaded, and we keep the contents of the log and the counter value
- but because Safari closed the network request as soon as we navigated forward, we get a WebSocket error notification
At least this is my understanding of the situation. It is perhaps worth spelling this out in the guide, maybe not to the screenshot level of detail though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @wbamberg, thanks for raising this.
I would love to hear from someone from WebKit if possible if they can share what Safari is doing with WebSocket.
I also noticed that Safari is behaving somehow differently from Chrome and Firefox.
It looks like pages using WebSocket without closing the connection via pagehide are still eligible for BFCache on Safari somehow.
From my tests it looked like the approach of closing the connection via pagehide and restarting the connection via pageshow was also working in Safari.
Can you please share the error you are getting?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correction: Safari is definitively throwing a readyState 3 error (CLOSED), although I see the connection restarting on Safari too on my test page (https://output.jsbin.com/sepojum).
Did you see the WebSocket connection not working on a BFCache restored page on Safari while testing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like pages using WebSocket without closing the connection via pagehide are still eligible for BFCache on Safari somehow.
This is what I would have expected, given the info in https://docs.google.com/document/d/1JtDCN9A_1UBlDuwkjn1HWxdhQ1H2un9K4kyPLgBqJUc/edit?tab=t.0#heading=h.yqabnzb880lx :
Safari caches such pages but cancels active network requests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not what I see in Safari, and I would like to know why we see different things here.
Ah, hold on. I've tried it again in Safari. When I navigate to a new page by typing the URL into the address bar and pressing Enter (or following a link), then press "Back", I get the behavior I describe (I am using gamespot.com as a test).
When I then press "Forward" to go back to the new page, then press "Back", I get the behavior your screenshot is showing.
I've tried it with a few other example pages, and get the same effects,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I see that as well. In 1b0a049 I've been less specific about what the browsers do, so I hope that means we don't have to worry about this.
Thanks for the review, Chris! I'm out until Tuesday but will look when I get back. |
Thanks everyone for your support for the WebSocket guide, sorry for late replying but I had been busy with lots of conflicting priorities these weeks. I will review this first thing next week and will review the changes. Thank you so much again for helping developers awareness about this opportunity for their sites when using WebSocket! |
Looks like this may fix #29029, though I'm not sure if completely. |
Co-authored-by: Chris Mills <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly LGTM but have made some comments.
Note that I'm far from a WebSockets expert, but I do know a fair bit about bfcache.
|
||
The back/forward cache, or {{glossary("bfcache")}}, enables much faster back and forward navigation between pages that the user has recently visited. It does this by storing a complete snapshot of the page, including the JavaScript heap. | ||
|
||
The browser pauses and then resumes JavaScript execution when a page is added to or loaded from the bfcache. This means that, depending on what the page is doing, it's not always safe for the browser to use the bfcache for the page. If the browser determines that it is not safe, then the page will not be added to the bfcache, and then the user will not get the performance benefit that it can bring. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would say "restored from the bfcache" rather than "loaded from the bfcache".
The bfcache isn't a "cache" really. More a preservation and restore of a page's state.
Different browsers use different criteria for adding a page to the bfcache. If a page has an active WebSocket connection, then: | ||
|
||
### Text data format | ||
- Firefox and Chrome will not add the page to the bfcache. | ||
- Safari will add the page to the bfcache, but will cancel any active network requests. This is likely to generate an error in your WebSocket connection, leading to the connection being closed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd probably be less explicit here as this ius subject to change.
For example Chrome is looking into whether just using a WebSocket really should disqualify a page from the bfcache. Or if we can still pout it in the bfcache but evict it only if a message is received on that WebSocket connection.
So maybe just leave as "Different browsers use different criteria for adding a page to the bfcache. For maximum support of the bfcache it is recommended to close any WebSocket clients on the {{domxref("Window.pagehide_event", "pagehide")}} event..."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I chose slightly different wording, hope this is OK? -> 1b0a049
This also gets us out of puzzling over Safari's behavior here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
}); | ||
``` | ||
|
||
Conversely, by listening for the {{domxref("Window.pageshow_event", "pageshow")}} event, you can seamlessly start the connection again when the page is loaded, or when it is retrieved from the bfcache. So in our example we will add all the code to initialize our WebSocket and set up its event listeners inside the `pageshow` event handler: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pageshow
is also fired on page load. So might wanna mention that (to avoid people opening a connection twice on pageload).
I think this would still be good as a short note (perhaps linking to the relevant section in this guide?). This updated guide is good, but it feels a little out of the way. |
-> 21592f9 (I deleted a previous note that I didn't think was particularly useful) |
Thanks for the reviews, people! I think this is ready for another look. |
Looks great now. One nit on the page_show on page load to consider, but other than that LGTM. |
…plications/index.md Co-authored-by: wbamberg <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wbamberg One minor language nitpick, which you can take or leave. Apart from that, LGTM!
files/en-us/web/api/websockets_api/writing_websocket_client_applications/index.md
Outdated
Show resolved
Hide resolved
…plications/index.md Co-authored-by: Chris Mills <[email protected]>
This came out of #40188, which wanted to talk about how to make WebSocket clients bfcache-compatible, and https://github.com/orgs/mdn/discussions/817, in which we find that the example that's supposed to underpin the current Writing WebSocket client applications page doesn't work.
In mdn/dom-examples#326 I added a new example that does work and is bfcache-compatible.
This PR updates the guide page to use that example as a backbone. I hope I haven't jettisoned anything too important from the old page.
I wonder if we ought to say more about URL schemes: this example uses
ws
which is maybe shouldn't, but it can't usewss
and still work with localhost AFAICT. But it could I think use a relative URL, and maybe should?I also don't talk about the
protocols
option, as we don't use it in the example and it would feel like quite a big digression. Maybe that would be better as a separate guide?On the differences between different browsers: this is mostly just what I have observed by testing. But this: https://docs.google.com/document/d/1JtDCN9A_1UBlDuwkjn1HWxdhQ1H2un9K4kyPLgBqJUc/edit?tab=t.0#heading=h.58d6ijfz2say is also a useful doc.
I wasn't sure about the "Security considerations" bit: I've kept it as-is but AFAIK mixed content just isn't allowed any more, and I couldn't find any more detail on "Most browsers now only allow secure WebSocket connections" (though I would love to know more about this, because if so it would be good to document it in BCD and describe it better here...).
I wouldn't be particularly averse to adding a note in https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API about bfcache-compatibility as well, if we think it merits more visibility than this.
If this merges I should file a follow-up to update https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_a_WebSocket_server_in_JavaScript_Deno with the new example, too.
cc @gilbertococchi and maybe @tunetheweb ?
Fixes #29029