HTML5的EventSource监听器是否可以监听所有事件?

18

我在JavaScript客户端应用程序中使用EventSource推送通知。我可以像这样附加事件侦听器:

source.addEventListener('my_custom_event_type', function(e) {
  console.log(e.data);
}, false);

我希望监视从服务器推送的所有事件(基本上是为了调试),因此如果发送了某些事件但没有事件监听器,我可以轻松找到它。我的意思是,我不想只是“忽略”所有没有绑定事件监听器的事件。

我期望像这样做:

source.addEventListener('*', function(e) {
  console.debug('Event with no listener attached: ', e);
}, false);

但是规范和像html5rocks这样的教程并没有明确说明这是否可能。

另一方面,可能有一些 Firefox/Chrome 扩展可以允许监视所有服务器事件或其他类似功能。这些东西对开发推送通知非常有帮助。

谢谢!

4个回答

31
我自己找到了一个解决方案,这还极大地改善了EventSource接口。 服务器端:不要发送事件类型,只需包含一个额外的数据字段(我总是使用JSON)。因此,不是:
event: eventName
data: {mykey: 'myvalue'}

我改成了从服务器发送:

data: {mykey: 'myvalue', eventName: 'eventName'}

客户端:现在我可以使用EventSource的onmessage回调函数,该函数在没有事件类型的每个消息上触发。

而对于绑定事件监听器,我创建了一个具有Backbone.Event功能的包装类。结果是:

// 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;
})();

现在有了这个包装类,我可以轻松地扩展功能,所有服务器发送的事件都可以很容易地被监控,而且通过扩展Backbone.Events,这个类中的事件处理功能也更加强大。

使用示例:

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');

现在我有一个易于处理、扩展、调试和测试的组件。


18
"Onmessage回调函数会在每个没有事件类型的消息上触发。" 这对我来说是非常有用的信息,谢谢。 - Matthew Walker
只是提醒一下:调用 onmessage = some_function; 和调用 addEventListener("message", some_function); 是完全相同的。这表明没有事件类型的消息与事件类型为 "message" 的消息是相同的。 - Ashitaka
你好 tothemario。不知何故,JSON.parse(event.data) 对我无效。您能否提供一下您的服务器端生成数据的方式吗:{mykey: 'myvalue', eventName: 'eventName'}?先谢谢了。 - pouzzler
感谢 tothemario 的回答,根据您的指导,我尝试了 source.addEventListener('eventName', MyHander, false); 这个方法可以在不使用包装器的情况下正常工作。(请查看下面的答案以获取完整示例) - Alex C

1

我知道这不是一个EventSource,但我正在寻找同样的东西(一种可以捕获所有传入事件而不知道它们类型的方法)。由于对发送这些事件的服务器没有任何控制,最终我只能使用XHR来编写代码,以防其他人遇到类似的情况:

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);
});

非常感谢Trey分享你的代码,对我来说非常有用。 - undefined

0
 <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>

2
这段代码不起作用,因为 onmessage 只处理没有类型的事件。 https://developer.mozilla.org/ru/docs/Web/API/EventSource - Grief

0

感谢用户tothemario提供的线索,帮助我解决了这个问题。

看起来你可以使用自定义类型将事件发送回浏览器,但是为了触发MESSAGE事件,你必须将监听器分配给新类型而不是message类型。

如果你查看下面的客户端代码,它会很好地说明问题。

为了上下文,我的服务器使用自定义类型CustomType发送事件。因此,我订阅该类型的事件侦听器,并添加另一个侦听器以捕获其他所有内容的message

在这个工作流程中,当一个带有CustomType的事件到达浏览器时,会触发不同的监听器。

 <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>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接