首页 > 解决方案 > JS SSE 回调不适用于 EventListener

问题描述

我正在使用 JS 库在我的 html 页面上流式传输服务器发送的事件:

    <html>
    <textarea rows='14' id="value" placeholder="anything you want here"></textarea>
    <button type="button" onclick="post(clip)">get</button>
    
    </html>
    <script type="text/javascript" src="sse.js"></script>
    <script>

url = "http://ac6ba97b046a5dcc677e.elb.us-east-1.amazonaws.com/myapi";
let textArea = document.getElementById("value");

function clip(){
  s = textArea.value;
  s = s.slice(0, -5);
  textArea.value = s;
  console.log('hello');
}

function post(callback){
    var v = String(textArea.value);
    console.log(v);
    var source = new SSE(url, {
      method: 'POST',
      headers: { 'Content-Type': 'text/plain' },
      payload: v
    });
    var arr = [];
    source.addEventListener('message', function (e) {
      arr.push(e.data);
      textArea.value = arr.map(el => el || " ").join('');
    });
    source.stream();
    callback();
}

</script>

单击按钮时,使用 POST 方法将数据发送到服务器,并使用从服务器接收到的数据填充文本框。我想在函数执行clip()后剪辑文本框中的文本。post()执行过程必须是这样的:

1. post() logs textArea value
2. source.stream() is executed, textbox populated
3. clip() clips last 5 characters and logs 'hello'

但我反而得到了这个:

1. post() logs textArea value
2. clip() clips last 5 characters and logs 'hello'
3. source.stream() is executed, textbox populated

出于某种原因,甚至在添加回调之后clip()才被执行。source.stream()

我正在使用的sse.js文件。

[编辑] 移动callback()到“消息”处理程序的末尾后,问题仍然存在:

function post(callback){
    var v = String(textArea.value);
    console.log(v);
    var source = new SSE(url, {
      method: 'POST',
      headers: { 'Content-Type': 'text/plain' },
      payload: v
    });
    var arr = [];
    source.addEventListener('message', function (e) {
      arr.push(e.data);
      textArea.value = arr.map(el => el || " ").join('');
      callback();
    });
    source.stream();
}

有谁知道这可能是什么原因造成的?

标签: javascriptcallbackevent-listenerserver-sent-events

解决方案


当您的脚本调用source.stream();时,它正在执行一个XMLHttpRequest.send()操作,默认情况下是异步的。

那么,发生了什么:

  1. 用户点击,并被post()调用
  2. SSE 对象及其事件侦听器已设置
  3. source.stream() 被调用,它将请求发送到服务器。
  4. callback()(即clip())被称为
  5. 服务器发回响应
  6. 您的message事件处理程序被调用
  7. textArea.value已设置

幸运的是,修复很简单:您只想callback()在收到消息时被调用。所以将 callback() 移动到message事件处理程序的末尾,而不是post().

它会在收到每个消息事件后执行此操作。如果您只希望它在第一个事件之后发生,您将需要实现一些逻辑来跟踪已收到多少事件。(如果只有一个消息事件,您应该使用 Ajax 调用,而不是 SSE/EventSource 调用。)

更新:评论中的讨论开始超出您原始问题的范围(简单地说,答案是“它是异步的,而不是同步的”)。但我认为值得在这里指出的是,每次用户单击按钮时,您都会设置一个新的 SSE 对象。每个 SSE 对象都有自己的专用 TCP/IP 套接字和自己的侦听器功能。这(通常)不是一个好主意:而是在您的 Web 应用程序启动时创建一次 SSE 连接。

而且,尽管您的SSEpolyfill 库允许使用 POST,但标准 SSE 不允许使用. 如果您只希望应用程序在用户按下按钮时轮询服务器,请考虑从使用 SSE 切换到普通 AJAX。


推荐阅读