Skip to main content

SSE: Server-Sent Events

· 4 min read
6gFjFF

Key Points

  • newEventSource(url)url 可以与当前网址同域,也可以跨域。跨域时,可以指定第二个参数,打开 withCredentials 属性,表示是否一起发送 Cookie。
  • EventSource实例的readyState属性,表明连接的当前状态。该属性只读,可以取以下值。
    • 0:相当于常量EventSource.CONNECTING,表示连接还未建立,或者断线正在重连。
    • 1:相当于常量EventSource.OPEN,表示连接已经建立,可以接受数据。
    • 2:相当于常量EventSource.CLOSED,表示连接已断,且不会重连。
  • source.onopen=function(event){...} 另一种写法 source.addEventListener('open',function(event){...},false)
  • 如果数据很长,可以分成多行,最后一行用 \n\n 结尾,前面行都用 \n 结尾。
  • 浏览器用 lastEventId 属性读取这个值。一旦连接断线,浏览器会发送一个 HTTP 头,里面包含一个特殊的 Last-Event-ID 头信息,将这个值发送回来,用来帮助服务器端重建连接。因此,这个头信息可以被视为一种同步机制。
  • 两种情况会导致浏览器重新发起连接:一种是时间间隔到期,二是由于网络错误等原因,导致连接出错
  • SSE 要求服务器与浏览器保持连接。对于不同的服务器软件来说,所消耗的资源是不一样的。Apache 服务器,每个连接就是一个线程,如果要维持大量连接,势必要消耗大量资源。Node 则是所有连接都使用同一个线程,因此消耗的资源会小得多,但是这要求每个连接不能包含很耗时的操作,比如磁盘的 IO 读写。

法一:使用 EventSource 监听 SSE

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>SSE Example</title>
</head>
<body>
<h1>SSE Data</h1>
<div id="data"></div>
<script>
const eventSource = new EventSource('http://localhost:3000/events');
const dataDiv = document.getElementById('data');

eventSource.onmessage = event => {
const data = JSON.parse(event.data);
dataDiv.innerHTML += `<p>${data.time}</p>`;
};

eventSource.onerror = err => {
console.error('EventSource failed:', err);
eventSource.close();
};
</script>
</body>
</html>

法二:getReader + TextDecoder + 递归读取

你可以直接使用 fetchReadableStreamfetch 原生支持流式数据处理,并且可以与 ReadableStreamTextDecoder 等 Web Streams API 无缝结合。

fetch('https://example.com/stream').then(response => {
const reader = response.body.getReader(); // 获取流的读取器
const decoder = new TextDecoder('utf-8'); // 创建解码器

reader.read().then(function processText({ done, value }) {
if (done) {
console.log('Stream complete');
return;
}

// 解码并处理数据
const chunk = decoder.decode(value, { stream: true });
console.log('Received chunk:', chunk);

// 继续读取下一个数据块
return reader.read().then(processText);
});
});

References

  1. Server-Sent Events 教程 by 阮一峰
  2. 小红书:一图看懂 3 种通信方式的区别
  3. 小红书:前后端实时通信方法解析!
  4. https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream
  5. https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder