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

如何在Angular中用RxJS处理NDJSON?需实现打字机效果

处理OpenAI流式NDJSON响应实现打字机效果

直接用Angular原生HttpClient就能处理,无需额外引入ndjson库,具体实现步骤如下:

  • 配置请求开启流式响应模式,指定responseType: 'text'和observe: 'events',获取原始文本数据流
  • 逐块接收响应数据,处理NDJSON的行分割逻辑(注意缓存截断的不完整行)
  • 解析每行JSON对象,提取OpenAI返回的内容片段
  • 拼接内容片段,实现打字机效果

完整代码示例

import { HttpClient, HttpEventType } from '@angular/common/http';
import { Component } from '@angular/core';

@Component({
  selector: 'app-openai-stream',
  template: `<div class="typed-content">{{ typedText }}</div>`
})
export class OpenaiStreamComponent {
  typedText = '';
  private incompleteLine = ''; // 缓存截断的不完整行

  constructor(private http: HttpClient) {}

  triggerOpenAIRequest() {
    const openAiApiUrl = 'https://api.openai.com/v1/chat/completions';
    const requestPayload = {
      model: 'gpt-3.5-turbo',
      messages: [{ role: 'user', content: '写一段关于Angular的入门介绍' }],
      stream: true // 必须开启流式返回开关
    };

    this.http.post(openAiApiUrl, requestPayload, {
      responseType: 'text',
      observe: 'events',
      headers: {
        'Authorization': 'Bearer YOUR_API_KEY',
        'Content-Type': 'application/json'
      }
    }).subscribe({
      next: (event) => {
        if (event.type === HttpEventType.DownloadProgress && event.partialText) {
          // 拼接缓存的不完整行与当前chunk文本
          const fullContent = this.incompleteLine + event.partialText;
          // 按行分割NDJSON内容
          const lines = fullContent.split('\n');
          // 最后一行可能不完整,缓存到下次处理
          this.incompleteLine = lines.pop() || '';

          lines.forEach(line => {
            // 过滤空行和结束标记行
            if (line.trim() && line.startsWith('data: ') && line !== 'data: [DONE]') {
              const jsonStr = line.replace('data: ', '');
              try {
                const responseData = JSON.parse(jsonStr);
                const contentFragment = responseData.choices[0]?.delta?.content;
                if (contentFragment) {
                  // 直接拼接实现基础打字机效果
                  this.typedText += contentFragment;
                }
              } catch (error) {
                console.error('NDJSON解析失败:', error);
              }
            }
          });
        }
      },
      error: (error) => {
        console.error('请求出错:', error);
      },
      complete: () => {
        // 处理最后剩余的不完整行
        if (this.incompleteLine.trim()) {
          const cleanLine = this.incompleteLine.replace('data: ', '');
          try {
            const responseData = JSON.parse(cleanLine);
            const contentFragment = responseData.choices[0]?.delta?.content;
            if (contentFragment) {
              this.typedText += contentFragment;
            }
          } catch (error) {
            console.error('剩余行解析失败:', error);
          }
          this.incompleteLine = '';
        }
      }
    });
  }
}

优化细节

如果需要更平滑的打字机动画,可以把直接拼接的逻辑改成逐字符延迟添加:

if (contentFragment) {
  contentFragment.split('').forEach((char, index) => {
    setTimeout(() => {
      this.typedText += char;
    }, 40 * index); // 40ms间隔,可根据需求调整速度
  });
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 00:03:18