首页 > 教程攻略 > ai资讯 >AI应用-消费流式输出

AI应用-消费流式输出

来源:互联网 时间:2026-07-21 07:48:17

聊到浏览器端消费流式数据,Fetch + ReadableStream 可以说是最原生的方案了。它不依赖第三方库,直接跟浏览器的 Streams API 打交道,灵活度非常高。

具体来说,这个方案有几个突出的优势:

  • 可以自由地使用 POST 请求,也能随心所欲地设置自定义请求头,这在对接后端 API 时非常关键
  • 对于返回的数据格式没有限制,不管是纯文本、JSON 片段还是其他二进制流,都能处理

下面这段代码就是一个通用的流式请求方法,已经封装成了一个异步生成器函数,可以直接拿来用:

/**
 * 通用流式请求方法 —— 返回一个异步可迭代对象
 * @param {string} url 请求地址
 * @param {RequestInit} [options] fetch 的配置项(method、headers、body、signal 等)
 * @returns {AsyncGenerator} 异步生成器,每次 yield 一个解码后的文本块
 */
async function* createStreamIterator(url, options = {}) {
  // 1. 发起 fetch 请求
  const response = await fetch(url, options);  // 2. 检查响应状态(可选但建议)
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }  // 3. 获取 reader 和 decoder
  const reader = response.body.getReader();
  const decoder = new TextDecoder('utf-8', { stream: true });  // 4. 逐块读取并 yield 解码后的文本
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    yield decoder.decode(value, { stream: true });
  }
}// 外部调用
// 结合取消(使用 AbortController)
const controller = new AbortController();
const iterator = createStreamIterator('/api/stream', { signal: controller.signal });// 在需要时取消请求
// controller.abort();for await (const chunk of iterator) {
  // 处理 chunk
}

代码的逻辑其实很清晰:先发起 fetch 请求,拿到响应对象后,通过 response.body.getReader() 获取一个读取器。然后进入一个循环,逐块读取数据,每次读取到的 Uint8Array 字节流,通过 TextDecoder 解码成字符串,再 yield 出去。

值得注意的一点是,TextDecoder 在初始化时传入了 { stream: true } 选项。这个参数的作用是告诉解码器,输入的数据是分块到达的流式数据,它会在内部处理好跨块字符的边界问题,避免出现乱码。

至于外部调用,代码里也给出了一个配合 AbortController 实现取消请求的示例。在实际场景中,比如用户手动关闭对话或页面切换时,就可以通过 controller.abort() 及时中断流式请求,避免资源浪费。