Reputation: 5899
We have an SSE (Server-Sent Events) connection open in JavaScript which can time to time get closed, either because of server restarts or other causes. In that case it would be good to reestablish the connection. How to do it? Is there a way to find out on the client side that the connection was closed?
Here: https://developer.mozilla.org/en-US/docs/Web/API/EventSource I found only a way to close the connection, but no callback or a test method for determining whether the connection is still alive.
Thank you for your help.
Upvotes: 21
Views: 26007
Reputation: 555
It is best not to try to determine if the connection was closed. I do not think there is a way to do it. Server Side Events work differently in all of the browsers, but they all close the connection during certain circumstances. Chrome, for example, closes the connection on 502 errors while a server is restarted. So, it is best to use a keep-alive as others suggest or reconnect on every error. Keep-alive only reconnects at a specified interval that must be kept long enough to avoid overwhelming the server. Reconnecting on every error has the lowest possible delay. However, it is only possible if you take an approach that keeps server load to a minimum. Below, I demonstrate an approach that reconnects at a reasonable rate.
This code uses a debounce function along with reconnect interval doubling. It works well, connecting at 1 second, 4, 8, 16...up to a maximum of 64 seconds at which it keeps retrying at the same rate.
function isFunction(functionToCheck) {
return functionToCheck && {}.toString.call(functionToCheck) === '[object Function]';
}
function debounce(func, wait) {
var timeout;
var waitFunc;
return function() {
if (isFunction(wait)) {
waitFunc = wait;
}
else {
waitFunc = function() { return wait };
}
var context = this, args = arguments;
var later = function() {
timeout = null;
func.apply(context, args);
};
clearTimeout(timeout);
timeout = setTimeout(later, waitFunc());
};
}
// reconnectFrequencySeconds doubles every retry
var reconnectFrequencySeconds = 1;
var evtSource;
var reconnectFunc = debounce(function() {
setupEventSource();
// Double every attempt to avoid overwhelming server
reconnectFrequencySeconds *= 2;
// Max out at ~1 minute as a compromise between user experience and server load
if (reconnectFrequencySeconds >= 64) {
reconnectFrequencySeconds = 64;
}
}, function() { return reconnectFrequencySeconds * 1000 });
function setupEventSource() {
evtSource = new EventSource(/* URL here */);
evtSource.onmessage = function(e) {
// Handle even here
};
evtSource.onopen = function(e) {
// Reset reconnect frequency upon successful connection
reconnectFrequencySeconds = 1;
};
evtSource.onerror = function(e) {
evtSource.close();
reconnectFunc();
};
}
setupEventSource();
Upvotes: 6
Reputation: 7086
EventSource API now has three event handlers:
These should be enough to handle everything you need on the client side.
Something like this:
const ssEvent = new EventSource( eventUrl );
ssEvent.onopen = function (evt) {
// handle newly opened connection
}
ssEvent.onerror = function (evt) {
// handle dropped or failed connection
}
ssEvent.onmessage = function (evt) {
// handle new event from server
}
Ref: mozilla.org : EventSource : Event handlers
Browser support for EventSource API: onopen - caniuse.com
Upvotes: 11
Reputation: 28928
If the connection is closed (in such a way that the browser can realize it), it will auto-connect. And it tends to do this quickly (default is 3 seconds in Chrome, 5 seconds in Firefox). readyState
will be CONNECTING (0) while it is doing this. It is only ever CLOSED (2) if there was some problem connecting in the first place (e.g. due to a CORS issue). Once CLOSED, it does not retry.
I prefer to add a keep-alive mechanism on top, as the browser cannot always detect dead sockets (not to mention a remote server process that is locked up, etc.). See ch.5 of Data Push Apps with HTML5 SSE for detailed code, but basically it involves having the server send a message every 15 seconds, then a JavaScript timer that runs for 20 seconds, but is reset each time a message is received. If the timer ever does expire, we close the connection and reconnect.
Upvotes: 18
Reputation: 48505
Check readyState
property:
var es = new EventSource();
// Сheck that connection is not closed
es.readyState !== 2;
// or
es.readyState !== EventSource.CLOSED;
Upvotes: 4