SSE 和 EventSource 使用
· 3 min read
SSE
规范参考 SSE(Server Send Event):
:
开头表示此行是注释, 不是数据- 每条消息之间用
\n\n
分割, 比如以下是两条消息, 第一条消息内容是hello
, 第二条消息的内容是world
data: hello
data: world
但是以下是一条消息, 消息内容是 hello\nworld
:
data: hello
data: world
event:
是可以省略的,缺省状态下事件类型是message
, 事件类型可以自定义, 或者只有事件发送
event: message
data: hello
event: close
示例
服务器端输出如下:
node.js
const rs = new ReadableStream({
async start(ctrl) {
const encoder = new TextEncoder();
ctrl.enqueue(encoder.encode(": this line is comment\n\n"));
ctrl.enqueue(encoder.encode("event: message\ndata: some text\n\n"));
ctrl.enqueue(encoder.encode("data: another message\n"));
ctrl.enqueue(encoder.encode("data: with two lines"));
}
});
return new Response(rs, {
headers: {
Connection: "keep-alive",
"Content-Encoding": "none",
"Cache-Control": "no-cache, no-transform",
"Content-Type": "text/event-stream; charset=utf-8"
}
});
浏览器接收到的流
: this line is comment
event: message
data: some text
data: another message
data: with two lines
对于以上流, 使用 EventSource
实际会接收到 3 条消息:
- 第一条以冒号开头, 表示仅仅是一个注释, 有时候用于保持客户端和服务器端持续连接(类似于心跳)
- 第二条的消息内容是
some text
- 第三条的消息内容是
another message\nwith two lines
EventSource
浏览器端可使用 new EventSource(url, { withCredentials: true }).onmessage
接收服务器端的消息, 但是这种只支持 GET
请求.
可以使用 fetch
接收 SSE 消息:
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "text/event-stream"
},
body: JSON.stringify({
/** 请求体内容 */
})
});
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
while (true) {
const {value, done} = await reader.read();
if (done) {
break;
}
console.log("value: ", value); // 这里需要自己根据规范处理 data event 等
}
或者直接使用第三方类库 @microsoft/fetch-event-source
const ctrl = new AbortController();
fetchEventSource(url, {
method: "POST",
body: JSON.stringify({}),
signal: ctrl.signal,
credentials: "include",
openWhenHidden: true, // fetchEventSource 默认会监听 visibleChange 事件, 传 `true` 禁用该行为
onmessage(msg) {},
onclose() {},
onerror(err) {
// 这里 abort 是防止自动重试, 根据自己需求设置
ctrl.abort();
}
});