JavaScript 标准提供了 EventSource
接口,用于与服务器发送事件(Server-Sent Events,SSE)接口进行通信。
1 2 3 4 5 6 7 8 9
| var evtSource = new EventSource("sse.php"); var eventList = document.querySelector("ul");
evtSource.onmessage = function (e) { var newElement = document.createElement("li");
newElement.textContent = "message: " + e.data; eventList.appendChild(newElement); };
|
上面的代码片段来源于 MDN,展示了 EventSource
的基本用法:通过后端接口 "sse.php"
接收事件,并在 onmessage
回调中处理事件(本例中是将事件数据添加到页面的列表中)。
然而,EventSource 存在一些局限性,最明显的是它仅支持连接到 GET 接口,无法使用 POST 等其他 HTTP 方法,也无法通过 HTTP Body 传输数据。这对于一些需要通过 POST 请求发送参数的应用场景会受到限制。
在生成式 AI 应用中,OpenAI 的对话接口支持流式传输数据,这种传输方式通常实现为 SSE。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import OpenAI from "openai";
const openai = new OpenAI();
async function main() { const stream = await openai.chat.completions.create({ model: "gpt-4o-mini", messages: [{ role: "user", content: "Say this is a test" }], stream: true, }); for await (const chunk of stream) { process.stdout.write(chunk.choices[0]?.delta?.content || ""); } }
main();
|
上述代码是 OpenAI 提供的 JavaScript SDK 中流式接口的示例,返回一个异步生成器,通过 for await…of 语法逐步处理返回的消息流。
在某些场景下,前端可能不会直接使用 OpenAI 的 SDK(例如出于安全原因不希望在浏览器中暴露 API key),而 OpenAI 的接口是通过 POST 方法发送 JSON 数据,因此不能直接使用 EventSource 来接收流式数据。这时的解决方案是通过 fetch 获取接口的响应 ReadableStream,手动解析流并封装为一个异步生成器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| async function* chatStream(url, body) { const resp = await fetch(url, { method: "POST", body: JSON.stringify({ body, }), }); const reader = resp.body.getReader(); let line = ""; while (true) { const { value, done } = await reader.read(); if (done) break; const decoder = new TextDecoder(); const text = decoder.decode(value, { stream: true }); line += text; if (line.endsWith("\n\n")) { yield line; line = ""; } } }
|
该代码展示了如何通过 fetch 发起 POST 请求,并通过 ReadableStream 解析服务器的流式响应。每当接收到完整的一段消息(以 \n\n
结尾),将其作为一条独立的消息返回给调用者。这样可以有效地模拟 EventSource 的行为,支持更多 HTTP 方法和自定义请求。