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.
event-source.js
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.
event-source.php
<?php
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:
event-source.html
Open your console.
<script src="event-source.js"></script>
Comments
We moved off of Disqus for data privacy and consent concerns, and are currently searching for a new commenting tool.
EventSource does look interesting but pretty much is just an easier way to get your long polling done in newer browsers. I guess it’s push in the sense until you send the header and exit the script nothing is done on the browser side apart from wait (see my fork: https://gist.github.com/100…. So potentially it could wait until the server decides to send something, but I can’t figure out a way to send multiple messages (with delays) with one connection?
One thing to note is you can’t get Firebug working in Aurora 6 to test this out on. I did then figure out my version of Chrome (11) supports EventSource and tested it there!
Thanks for updated demo and explanation! In Firefox/Aurora 6, instead of Firebug, use the \”Web Console\” for logging output. Tools > Web Developer > Web Console 🙂
Ah thanks for that 😀
Small correction here. In event-source.js it says \”// seeing readyState CONNECTING (1) ( or CLOSED (2) )\” however, the spec states:
// ready state
const unsigned short CONNECTING = 0;
const unsigned short OPEN = 1;
const unsigned short CLOSED = 2;
@Mike whoops! thanks dude! Fixed.
Ah a further look into output buffering/flushing, I managed to get PHP to send multiple messages with a time delay in between: https://gist.github.com/100…
Nice! But, you’ll still notice that an \”open\” event is fired for every set of message events that are produced by the end point
In my example the connection is never closed (at least in my own tests) and the open event is only trigger once, followed by subsequent message events every second.
Of course if the script were to exit for whatever reason, the open event would be once again trigger upon reconnection. Is that what you mean?
Luke – that’s pretty awesome, and I’m stoked that you figured out a way to do it in which a push effect is perceived.
The only outstanding issue is that it required way too much \”setup\” from the server end point – which is not to say that it is incorrect – it just seems like a poorly drafted spec if this is what is required to achieve a true push.
For sure it’s a bit annoying to set up PHP in this way, then again PHP wasn’t design for this kind of thing. I’m sure node.js would be an awesome fit here somewhere.
Yes, doing it with PHP is painful, but with NodeJS it’s dead easy! I actually did an EventSource push example a few months back, it’s a node server that runs a sequencer to show the fingering of Smoke on the Water, you can run it in multiple windows to see that they all go in sync. All the files are in http://niiden.com/eventsour… , and yeah, sorry, I don’t have the time to change the code right now, but it does it on port 80, so you have to go sudo. :/
Would that work with your polyfill, btw?
You are not suppose to close the connection. Instead, run an infinite while loop on the server and use flush() to immediately push data to the client. Use sleep() to put some delay in the while loop.
If the connection is closed, EventSource falls back to long polling which is a documented behaviour in the spec.
@Niloy, see Luke’s gist.
Well, why doesnt Luke update his post then? EventSource is perfectly capable of Server push. He is sending the wrong message.
Read all the comments @Niloy 🙂 Or, check out: https://gist.github.com/100…