Receive Updates from the Server Using the EventSource

By Rob Gravelle

You might think that the EventSource is part of the recent HTML5 spec. Actually, it's part of the Server-Sent Events (SSE) API proposed by the Web Hypertext Application Technology Working Group (WHATWG). It's a standard that includes to a mechanism for servers to push content to the client. Opera was the first browser to include the new technology back in 2006. It's pretty much supported by all major browsers now except for one hold out. You guessed it, that would be Internet Explorer (IE). In today's article, we'll learn how to use the EventSource to establish a connection between the browser and server and initiate a data transmission.

On the Server - A PHP Example

Just about any good server language can be utilized, but I'm going to use PHP here today. I'm sure that you'll find it just as easy to accomplish with your preferred language.

Opening the Lines of Communication

The syntax for communicating with the client (i.e. the browser) is surprisingly easy. It all boils down to a special Content-Type header called "text/event-stream". Once the browser receives that, it's ready to start listening for more data streams. While not a requirement, it's also a good idea to turn off caching:

<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');

The Anatomy of a Message

The beginning of each stream broadcast is identified by the text "data: " - e.g., "data: This is information from the server.". Each line must also be terminated by a newline character. PHP has a special constant for that called PHP_EOL. Finally, an extra newline denotes the end of that broadcast. Here's some code that sends the time to the client:

<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache'); // recommended to prevent caching of event data.

function sendMsg($msg) {
  echo "data: $msg" . PHP_EOL;
  echo PHP_EOL;

  flush();
}

sendMsg('server time: ' . date("h:i:s"));
?> 

On the Client

When data is pushed from the server, the onmessage handler fires and its context may be accessed via the EventSource data property. Should the connection to the server be severed, the browser will automatically attempt to reconnect to the source after three seconds. The reconnection timeout can be changed by your server script. We'll get into the particulars of that in a bit.

Testing for Browser Support

As mentioned earlier, Internet Explorer does not support the EventSource so we should check for support before attempting to use it. As always, browser sniffing is NOT the way to go. Instead, we should test for the EventSource object using a comparison to the "undefined" type. Some people use the excellent typeof() function for this purpose, but you can just as easily test the object directly as long as you treat it as a property of the global window object. The results of the comparison are then stored for later use:

var esSupport = false, es, result;

function init() {
  esSupport = (window.EventSource !== undefined);
  result = document.getElementById("result");
  document.getElementById("btnStartTheClock").onclick = startTheClock;
  document.getElementById("btnStopTheClock") .onclick = stopTheClock;
}
window.onload = init;

Establishing a Connection to the Server

While it's true that SSEs are meant for server pushes, the process must be initiated by the client. After that, the connection will remain active until one of the parties calls it quits. It all starts when a new EventSource is instantiated with the path to the server component. If you are launching the process from a button click, you have to be careful that the connection is only set up once. To start receiving broadcasts, hook into the EventSource's onmessage() event. The EventSource object will be passed to your handler function, so that you can read its data property.

There is an onerror() event handler, but it's not as useful as you might think. It can however tell us some things about what's happening, thanks to the EventSource's readyState property. For instance, a value of EventSource.CONNECTING means that the connection was lost and the EventSource is attempting to reconnect. If something goes very wrong, a value of EventSource.CLOSED will let you know. Other than that, you can't get much information as there is no error object to query. Instead, it is the event object itself that is passed to the handler. The EventSource may be accessed via the eventSource or eventTarget properties, since both point to the same object.

function startTheClock() {
  if(esSupport) {
    if (es === undefined) {
      es = new EventSource("time_service.php");
      es.onmessage = function(event) {
        result.innerHTML += event.data + '<br>';
      };
      es.onerror = function(e) {
        e = e || event, msg = '';
        
        switch( e.target.readyState ){
          // if reconnecting
          case EventSource.CONNECTING:
            msg = 'Reconnecting…';
            break;
          // if error was fatal
          case EventSource.CLOSED:
            msg = 'Connection failed. Will not retry.';
            break;
        }
        result.innerHTML += '<span style="color:red;">' + msg + "</span><br>";
      };
    }
  }
  else {
    result.innerHTML = "Your browser doesn't support server-sent events.";
  }
}

Closing the Connection

Just as the client is responsible for initiating communication with the server, it is also tasked with ending it. One call to the EventSource's close() method is all it takes:

function stopTheClock() {
  if(esSupport) { 
    es.close(); 
    result.innerHTML += "Clock Stopped.<br>";
  }
}

Changing the Default Timeout Value

You can change the default timeout from the server by including a line beginning with "retry:", followed by the number of milliseconds to wait before trying to reconnect. The following code changes the timeout to 10 seconds:

echo "retry: 10000" . PHP_EOL; 
echo "data: $msg" . PHP_EOL; 

Here are the results when called from the browser:

server time: 02:22:37
Reconnecting…
server time: 02:22:47
Reconnecting…
server time: 02:22:57
Reconnecting…
server time: 02:23:07
Reconnecting…
server time: 02:23:17
Reconnecting…
Clock Stopped.

Conclusion

You can see in that last example that there is a lot of reconnecting going on. That's happening because the php script is executing once and then terminating. In the next article, we'll learn how to use a loop to keep the connection alive as well as how to use the "event:" and "id:" line prefixes.



Make a Comment

Loading Comments...

  • Web Development Newsletter Signup

    Invalid email
    You have successfuly registered to our newsletter.
  •  
  •  
  •