Server-Sent Events(简称 SSE)是 HTML5 标准中的一个 API,提供了服务器向浏览器主动推送数据的机制。

SSE 与 WebSocket 的比较

SSE 与 WebSocket 类似,都允许浏览器订阅服务器端的数据源。当有新数据生成时,服务器会发送通知给浏览器,以实时更新页面内容。然而,两者在使用场景上有所不同。SSE 更适合单向通信的场景,如股票行情、新闻推送、社交媒体状态更新和 AI 聊天机器人等。而 WebSocket 提供双向通信,适合需要双向交互的应用,如多人游戏和聊天室。

由于 WebSocket 使用双向全双工连接,需要浏览器和服务器都支持 WebSocket 协议。相比之下,SSE 是基于 HTTP 协议的传输方式,无需额外的通信协议支持。此外,SSE 还具有一些 WebSocket 所不具备的特性,如自动重连、事件 ID 和发送任意事件等,这些特性使 SSE 在某些应用场景中更具优势。

准备工作

为了便于验证以下客户端示例代码,建议先克隆 https://github.com/y1j2x34/sse-server-example 项目到本地,并运行 SSE 服务。

浏览器端使用 SSE 的方法

使用 EventSource API

首先,创建一个 EventSource 对象:

const url = "http://localhost:10086/sse";
const source = new EventSource(url, {
    withCredentials: false
});

参数说明:

url:远程资源的地址。
withCredentials:默认为 false,表示跨域时是否包含 credentials 凭据。

EventSource 对象创建后会立即发起一个 GET 请求,并自动携带 Accept: text/event-stream 请求头。

连接建立后,当接收到数据时,会触发 message 事件,通过 e.data 可以获取服务器发送的数据:

source.addEventListener('message', e => {
    console.log(e.data);
});

此外,还有连接建立和关闭的事件:

source.addEventListener('open', e => {
    console.log('已建立连接');
});

source.addEventListener('error', e => {
    if (source.readyState === EventSource.CLOSED) {
        console.log('连接已关闭');
    }
});

EventSource 的状态值:

EventSource.CONNECTING:连接正在进行。
EventSource.OPEN:连接已建立。
EventSource.CLOSED:连接已关闭。

需要注意的是,SSE 连接中断或服务器返回数据失败时,都会触发 error 事件,而非 close 事件。根据 SSE 规范,连接中断后会自动重连,重连间隔时间可以在后端响应数据中指定。

使用 fetch API

通过 fetch API 发起 SSE 请求的示例如下:

(async () => {
    const response = await fetch('http://127.0.0.1:10086/sse', {
        method: 'POST',
        headers: {
            'Accept': 'text/event-stream'
        }
    });
    const reader = response.body.getReader();
    while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        const chunk = new TextDecoder().decode(value);
        const json = chunk.replace(/^data:\s+/, '').trim();
        console.log(json);
    }
})();

使用 @microsoft/fetch-event-source

参考:https://github.com/Azure/fetch-event-source

import { fetchEventSource } from '@microsoft/fetch-event-source';

class RetriableError extends Error {}
class FatalError extends Error {}

fetchEventSource('http://127.0.0.1:10086/sse', {
    async onopen(response) {
        if (response.ok && response.headers.get('content-type') === 'text/event-stream') {
            return;
        } else if (response.status >= 400 && response.status < 500 && response.status !== 429) {
            throw new FatalError();
        } else {
            throw new RetriableError();
        }
    },
    onmessage(msg) {
        if (msg.event === 'FatalError') {
            throw new FatalError(msg.data);
        }
    },
    onclose() {
        throw new RetriableError();
    },
    onerror(err) {
        if (err instanceof FatalError) {
            throw err;
        }
    }
});
posted on   y1j2x34  阅读(273)  评论(0编辑  收藏  举报
努力加载评论中...

点击右上角即可分享
微信分享提示