You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在JavaScript中实现C# JsonSerializer.DeserializeAsyncEnumerable等效功能?

在JavaScript中实现类似C# JsonSerializer.DeserializeAsyncEnumerable的功能

当.NET控制器返回IAsyncEnumerable时,低速yield会和JS的response.body.getReader().read()一一对应,但全速运行时数据会被缓冲成JSON块。要实现和C# JsonSerializer.DeserializeAsyncEnumerable一样的功能,核心是流式解析JSON序列,逐个提取元素,而非等待整个响应完成再解析。

核心实现思路

  1. 逐块读取响应流,维护缓冲区存储未完成的JSON片段
  2. 每次读取新块后,尝试从缓冲区解析完整的JSON元素
  3. 解析出的元素逐个返回(类似C#的yield),剩余未解析片段留在缓冲区等待下一块数据
  4. 流结束后处理最后剩余的缓冲区内容

代码实现

async function* deserializeAsyncEnumerable(response) {
  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  let buffer = '';
  // 匹配JSON对象结尾,兼容带逗号/不带逗号的格式(适配.NET流式输出)
  const itemPattern = /(\{[^{}]*\})\s*,?\s*/g;

  // 处理JSON数组的首尾包裹符
  function trimArrayWrapper() {
    buffer = buffer.trim();
    if (buffer.startsWith('[')) buffer = buffer.slice(1).trim();
    if (buffer.endsWith(']')) buffer = buffer.slice(0, -1).trim();
  }

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    buffer += decoder.decode(value, { stream: true });
    trimArrayWrapper();

    let match;
    // 循环提取所有完整的JSON元素
    while ((match = itemPattern.exec(buffer)) !== null) {
      try {
        const item = JSON.parse(match[1]);
        yield item;
      } catch (err) {
        // 解析失败说明片段不完整,回退索引并跳出循环
        itemPattern.lastIndex = match.index;
        break;
      }
    }
    // 更新缓冲区,保留未解析的部分
    buffer = buffer.slice(itemPattern.lastIndex);
  }

  // 处理最后剩余的内容
  trimArrayWrapper();
  if (buffer) {
    try {
      const item = JSON.parse(buffer);
      yield item;
    } catch (err) {
      console.error('Failed to parse final JSON fragment:', err);
    }
  }
}

使用示例

async function processStreamedData() {
  const response = await fetch('/your-api-endpoint');
  if (!response.ok) {
    throw new Error(`Request failed: ${response.status}`);
  }

  // 逐个处理流式返回的元素
  for await (const dataItem of deserializeAsyncEnumerable(response)) {
    console.log('Received item:', dataItem);
    // 这里添加业务逻辑,比如渲染页面、存储数据等
  }
}

// 执行流式处理
processStreamedData().catch(err => console.error(err));

关键细节说明

  • 缓冲区与分块解析:服务器全速发送时数据会合并成大块,甚至单个JSON元素可能拆分在多个块中,缓冲区用来拼接片段,确保仅解析完整元素
  • 正则匹配调整:如果接口返回换行分隔的JSON(NDJSON),可将正则改为/^.*$/gm简化处理
  • TextDecoder的stream模式:开启{ stream: true }避免多字节字符被截断导致解码错误
  • 数组包裹处理:默认适配.NET IAsyncEnumerable返回的[{},{},...]格式,若接口返回无包裹的单个对象,可删除trimArrayWrapper相关逻辑

内容的提问来源于stack exchange,提问作者McMurphy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 12:47:54