【效率翻倍】再也不用手动解析 AI 数据流!用这个库优雅地消费 SSE 事件流!

86 阅读2分钟

SSE Stream Parser

一个用于处理 Server-Sent Events (SSE) 流数据的 TypeScript 库,支持将 SSE 协议的 ReadableStream 转换为结构化数据,并提供灵活的流处理能力。

🎯 在线演示

🚀 点击查看github在线演示

在线演示包含了三个完整的使用示例:

  • 异步迭代器模式 (for await...of)
  • 订阅模式 (subscribe)
  • 自定义转换流处理

✨ 特性

  • 🚀 TypeScript 原生支持 - 完整的类型定义和类型安全
  • 📡 SSE 协议解析 - 自动解析 SSE 格式的流数据
  • 🔄 灵活的数据转换 - 支持自定义 TransformStream 进行数据转换
  • 🎯 多种消费方式 - 支持异步迭代器、订阅模式等多种数据消费方式
  • 🛡️ 错误处理 - 内置错误处理和资源清理机制
  • 📦 零依赖 - 基于现代浏览器原生 API,无外部依赖

📦 安装

npm install sse-stream-parser
yarn add sse-stream-parser
pnpm add sse-stream-parser

🚀 快速开始

基础用法 - SSE 协议解析

import SseStreamParser from 'sse-stream-parser';

// 从 fetch 获取 SSE 流
const response = await fetch('/api/sse-endpoint');
const stream = SseStreamParser({
  readableStream: response.body!,
});

// 方式1: 使用 for await...of 异步迭代
for await (const event of stream) {
  console.log('SSE Event:', event);
  // 输出: { event: 'message', data: '{"id": "1", "content": "Hello World"}'}
}

订阅模式

// 方式2: 使用订阅模式
const unsubscribe = stream.subscribe(
  (event) => {
    console.log('收到事件:', event);
     // 输出: { event: 'message', data: '{"id": "1", "content": "Hello World"}'}
  },
  () => {
    console.log('流已结束');
  },
);

// 需要时取消订阅
// unsubscribe();

自定义数据转换

// 自定义转换流,将 SSE 数据转换为特定格式
const customTransform = new TransformStream<string, CustomData>({
  transform(chunk, controller) {
    try {
      const data = JSON.parse(chunk);
      controller.enqueue({
        timestamp: Date.now(),
        payload: data,
        processed: true,
      });
    } catch (error) {
      console.error('解析错误:', error);
    }
  },
});

const stream = SseStreamParser<CustomData>({
  readableStream: response.body!,
  transformStream: customTransform,
});

📚 使用场景

1. SSE 协议数据处理

何时使用: 当你需要处理符合 SSE 协议格式的流数据时

// SSE 格式数据示例:
// event: message
// data: {"type": "chat", "content": "Hello"}
// id: 123
//
// event: update
// data: {"status": "processing"}

const stream = SseStreamParser({
  readableStream: response.body!,
});

for await (const event of stream) {
  switch (event.event) {
    case 'message':
      handleMessage(JSON.parse(event.data));
      break;
    case 'update':
      handleUpdate(JSON.parse(event.data));
      break;
  }
}

2. 通用流数据解码

何时使用: 当你需要将任何协议的 ReadableStream 解码并转换为特定格式时

// 处理自定义协议的流数据
const jsonLinesTransform = new TransformStream<string, any>({
  transform(chunk, controller) {
    const lines = chunk.split('\n');
    for (const line of lines) {
      if (line.trim()) {
        try {
          controller.enqueue(JSON.parse(line));
        } catch (error) {
          console.error('JSON 解析错误:', error);
        }
      }
    }
  },
});

const stream = SseStreamParser({
  readableStream: response.body!,
  transformStream: jsonLinesTransform,
});

3. 实时数据流处理

// 处理实时聊天消息
const chatStream = SseStreamParser<ChatMessage>({
  readableStream: chatResponse.body!,
});

chatStream.subscribe(
  (message) => {
    updateChatUI(message);
  },
  () => {
    showConnectionClosed();
  },
);

// 处理实时日志流
const logStream = SseStreamParser<LogEntry>({
  readableStream: logResponse.body!,
});

for await (const logEntry of logStream) {
  appendToLogViewer(logEntry);
}

🌟 高级用法

错误处理

try {
  const stream = SseStreamParser({ readableStream: response.body! });

  for await (const event of stream) {
    try {
      processEvent(event);
    } catch (error) {
      console.error('处理事件时出错:', error);
    }
  }
} catch (error) {
  console.error('流处理出错:', error);
}

流的组合和管道

// 组合多个转换流
const preprocessor = new TransformStream<string, string>({
  transform(chunk, controller) {
    // 预处理数据
    const cleaned = chunk.replace(/\r\n/g, '\n');
    controller.enqueue(cleaned);
  },
});

const parser = new TransformStream<string, ParsedData>({
  transform(chunk, controller) {
    // 解析数据
    const parsed = parseCustomFormat(chunk);
    controller.enqueue(parsed);
  },
});

// 创建处理管道
const processedStream = response.body!.pipeThrough(new TextDecoderStream()).pipeThrough(preprocessor);

const stream = SseStreamParser({
  readableStream: processedStream,
  transformStream: parser,
});

性能优化

// 批量处理数据
let batch: SSEOutput[] = [];
const BATCH_SIZE = 10;

const stream = SseStreamParser({ readableStream: response.body! });

stream.subscribe((event) => {
  batch.push(event);

  if (batch.length >= BATCH_SIZE) {
    processBatch(batch);
    batch = [];
  }
});