UPDATE: With some serious modification to the server end point, Luke Morton was able to achieve a single open event that fires message events as responses are received via a “push” from the server: https://gist.github.com/1002722. By modifying the original example’s PHP end point, Luke’s approach was to omit the closing PHP tag (ensures that no return code is sent to the client), buffer and flush the output and wrap the whole program in a while loop that sleeps for one second to simulate delays in response activity.

Now that we’ve cleared that up, here is the original post:

Since Mozilla’s announcement this past weekend ( “Aurora 6 Is Here” ) and my own subsequent recap of previously published materials, there has been a noticeable uptick in attention towards the EventSource API, including a new polyfill for the feature.

As always, positive attention towards new JavaScript APIs is nothing but awesome — however — there seems to be a misunderstanding in the behaviour of an EventSource instance. Specifically, the idea that EventSource (in it’s current implementations) is capable of server-push, is incorrect. To demonstrate this fact, I’ve prepared a set of code examples, which can be downloaded here. Follow along with the comments in the code examples, to learn how the current implementations of the EventSource API actually work.

This is a quick look at a client that creates a single EventSource instance. The comments within are a paraphrasing of the spec production and the observed, testable behaviour.


document.addEventListener("DOMContentLoaded", function() {


  EventSource is nothing more then a glorified
  long polling machine. It will create HTTP requests
  to a provided url of the same origin
  (which in turn creates an `open` event ) until
  it sees a valid "text/event-stream" response body
  at which point a `message` event will be fired.

  This process will repeat until the EventSource is
  terminated my calling its close() method.

  no "data: " message from the server should result in long polling
  `open` events being fired, followed by zero `message` events


  var // declare localized references
  eventSrc  = new EventSource( "event-source.php" ),
  handler = function( event ) {

    console.log( [ event.type, new Date(), event, event.data ] );

  getReadyState = function( src ) {

    if ( src.readyState ) {
      // readyState is almost always 0, we're only interested in
      // seeing readyState OPEN (1) ( or CLOSED (2) )
      console.log( [ src.readyState, new Date() ] );

    setTimeout(function() {
      getReadyState( src );
    }, 1);

  console.log( eventSrc );

  // Setup event handlers
  [ "open", "message" ].forEach( function( name ) {

    eventSrc.addEventListener( name, handler, false );


  // Begin sampling the ready state
  getReadyState( eventSrc );

}, false);

Here we have a basic EventSource end point located on the server. The comments within will detail the behaviour that is created when a response body is not provided and when it is.


header("Content-Type: text/event-stream\n\n");

  To test the long polling thery, this response "stream"
  has no response body.

  From the client, EventSource will make an HTTP request
  for the url provided to the requesting instance. If no data body
  exists in the response, then no `message` event is fired.

  The client will continue to poll by creating new HTTP requests
  (that create `open` events on the client) until a valid
  response body ("data: [string]") is present.

  Open event-source.html in your browser, then open your console.
  you will see logged `open` events. Return to this
  file and uncomment the following line:

//echo "data: foo" . "\n\n";

  This will result in new `message` events being logged to the console.

  Notice that the `message` event is actually preceded by an `open`
  event, which (if you look in the network (or similar) tab of your
  console, you will see) a new HTTP request is created for

And a simple html file to run the whole thing on your localhost:


Open your console.
<script src="event-source.js"></script>