Reputation: 6299
I have push notifications in my JavaScript client app using EventSource. I can attach event listeners like this:
source.addEventListener('my_custom_event_type', function(e) {
console.log(e.data);
}, false);
But I want to monitor all events that are being pushed from the server (basically for debugging), so if some event is sent but it has no event listener I can easily find it. I mean, I don't want to just "ignore" all events that have no eventListeners binded.
I would expect to do something like this:
source.addEventListener('*', function(e) {
console.debug('Event with no listener attached: ', e);
}, false);
But the specification and tutorials like the one at html5rocks don't specify if this is possible or not.
In the other hand, it may be some firefox/chrome extension that allows to monitor all server events or something. Those things would really help on developing push notifications.
Thanks!
Upvotes: 19
Views: 22088
Reputation: 17024
Credit to user tothemario
above for the clue I needed to figure this out.
It appears that you CAN send events back to the browser with a custom type, but in order to trigger the MESSAGE event you must assign a listener to the new type rather than the message
type.
If you look at the client side code below it will hopefully illustrate.
For context, my server sends an event with the custom type CustomType
. Therefore I subscribe with an event listener to that type, and I add another listener for message
as a catch all for everything else.
In this workflow, an event that comes to the browser with the CustomType
a different listener fires.
<script type="text/javascript">
var CustomTypeList = [];
function EventSystemOpen(e) {
console.log("EventSystemOpen", e);
}
function EventSystemError(e) {
console.log("EventSystemOpen", e);
if (e.readyState == EventSource.CLOSED) {
//
}
}
function GotServerEventMessage(e) {
console.log("GotServerEventMessage", e);
}
function GotCustomType(e) {
CustomTypeList.push(JSON.parse(e.data));
console.log("Added CustomType", e, JSON.parse(e.data), CustomTypeList);
}
if (!!window.EventSource) {
var source = new EventSource('api/listen');
source.addEventListener('open', EventSystemOpen, false);
source.addEventListener('error', EventSystemError, false);
source.addEventListener('message', GotServerEventMessage, false);
source.addEventListener('CustomType', GotCustomType, false);
}
</script>
Upvotes: 0
Reputation: 5520
I know this isn't an EventSource, but I was looking for the same thing (a way to catch all incoming events without knowing their type). Without any control over the server sending these events, I ended up just writing it with an XHR, in case anyone else comes across this:
function eventStream(path, callback){
//Create XHR object
var xhr = new XMLHttpRequest();
//initialize storage for previously fetched information
var fetched='';
//Set readystatechange handler
xhr.onreadystatechange=function(){
//If the connection has been made and we have 200, process the data
if(xhr.readyState>2 && xhr.status==200){
//save the current response text
var newFetched=xhr.responseText;
//this is a stream, so responseText always contains everything
//from the start of the stream, we only want the latest
var lastFetch=xhr.responseText.replace(fetched, '');
//Set the complete response text to be removed next time
var fetched=newFetched;
//callback to allow parsing of the fetched data
callback(lastFetch);
}
};
//open and send to begin the stream;
xhr.open('GET', path, true);
xhr.send();
}
parseEvents=function(response){
var events=[];
//split out by line break
var lines=response.split("\n");
//loop through the lines
for(var i=0;i<lines.length;i++){
//each event consists of 2 lines, one begins with
//"name:", the other with "data"
//if we hit data, process it and the previous line
if(lines[i].substr(0, lines[i].indexOf(':'))=='data'){
//add this event to our list for return
events.push({
//get the event name
name: lines[i-1].split(':')[1].trim(),
//parse the event data
data: $.parseJSON(lines[i].substr(lines[i].indexOf(':')+1).trim())
});
}
}
//return the parsed events
return events;
};
evenStream('http://example.com/myEventPath', function(response){
var events=parseEvents(response);
});
Upvotes: 1
Reputation: 13
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js" type="text/javascript"></script>
<script>
var content = '';
if(typeof(EventSource)!=="undefined")
{
var source = new EventSource("demo_sse.php");
source.onmessage = function(event)
{
content+=event.data + "<br>";
$("#result").html(content);
};
}
else
{
$("#result").html("Sorry, your browser does not support server-sent events...");
}
</script>
Upvotes: 0
Reputation: 6299
I figure out a solution myself, that also improves tremendously the EventSource interface.
Server side: Do not send the event type, just include an additional data field (having that I always use json). So instead of
event: eventName
data: {mykey: 'myvalue'}
I send this from the server instead:
data: {mykey: 'myvalue', eventName: 'eventName'}
Client side: Now I can use EventSource onmessage callback, that is fired on every message that does not have an event type.
And for bind event listeners, I create a wrapper class with Backbone.Event functionality. The result:
// Server Sent Events (Event Source wrapper class)
var MyEventSource = (function() {
function MyEventSource(url) {
var self = this;
_.extend(this, Backbone.Events);
this.source = new EventSource(url);
this.source.onmessage = function(event) {
var data, eventName;
var data = JSON.parse(event.data);
var eventName = data.eventName; delete data.eventName;
// Now we can monitor all server sent events
console.log('app.server.on ', eventName, '. Data: ', data);
self.trigger(eventName, data);
};
}
return MyEventSource;
})();
Now with this wrapper class, I can easily extend the functionality, all server sent events can be easily monitored and thanks to extending Backbone.Events the event handling in this class is much more powerful.
Usage example:
var source = new MyEventSource('url/of/source');
// Add event listener
source.on('eventName', function(data) {
console.log(data);
});
// Fire a event (also very useful for testing and debugging!!)
source.trigger('eventName', { mykey: 'myvalue' });
// Unbind event listener (very important for complex applications)
source.off('eventName');
Now I have a component that is easy to handle, extend, debug and test.
Upvotes: 32