AI 智能助手项目面试技术要点总结(前端部分)

1. 流式音频播放的实现

**问题:**如何实现TTS音频的流式播放,而不是等待所有音频数据接收完才播放?

解决方案:

  • 使用 MediaSource API实现边接收边播放

  • 创建 MediaSource 对象并生成 Blob URL 绑定到 audio 元素

  • 在

    复制代码
    sourceopen

    事件中创建 SourceBuffer,指定音频格式(audio/mpeg)

  • 通过 ReadableStream 逐块读取后端流式返回的音频数据

  • 使用

    复制代码
    sourceBuffer.appendBuffer()

    追加数据,监听

    复制代码
    updateend

    事件确保顺序追加

  • 数据接收完成后调用

    复制代码
    mediaSource.endOfStream()

    结束流

关键代码:

复制代码
const mediaSource = new MediaSource();
avatarAudio.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', async () => {
  const sourceBuffer = mediaSource.addSourceBuffer('audio/mpeg');
  const reader = stream.getReader();
  while (true) {
    const {value, done} = await reader.read();
    if (done) break;
    await new Promise(resolve => {
      sourceBuffer.appendBuffer(value);
      sourceBuffer.addEventListener('updateend', resolve, {once: true});
    });
  }
});

Copyjavascript


2. 音频播放时跳过开头的问题

**问题:**流式音频播放时跳过了开头部分,从中间某句话才开始播放。

解决方案:

  • **根本原因:**在音频数据还未解码完成时就调用了

    复制代码
    play()

    ,导致跳过未准备好的部分

  • **解决思路:**先追加第一个数据块,等待

    复制代码
    canplay

    事件触发后再播放

  • 检查

    复制代码
    audio.readyState >= 3

    判断是否已经可以播放,避免事件监听器未触发

  • 避免在

    复制代码
    sourceopen

    回调开始就等待

    复制代码
    canplay

    ,会造成死锁(需要数据才能触发 canplay,但数据追加在回调里)

关键代码:

复制代码
// 先追加第一个数据块
const {value: firstChunk} = await reader.read();
await new Promise(resolve => {
  sourceBuffer.appendBuffer(firstChunk);
  sourceBuffer.addEventListener('updateend', resolve, {once: true});
});

// 等待音频准备好
await new Promise(resolve => {
  if (audio.readyState >= 3) resolve();
  else audio.addEventListener('canplay', resolve, {once: true});
});

audio.play();

Copyjavascript


3. 请求取消与资源管理

**问题:**AI流式输出时频繁触发TTS请求,导致多个音频重叠播放或资源泄漏。

解决方案:

  • 使用 AbortController管理 fetch 请求,新请求到来时取消上一个未完成的请求

  • 使用全局变量保存当前的 MediaSource 和 AbortController 引用

  • 在新请求开始前:

    • 调用

      复制代码
      abortController.abort()

      取消网络请求

    • 调用

      复制代码
      mediaSource.endOfStream()

      结束流

    • 清空 audio 元素的 src 并暂停播放

    • 停止视频动画

  • 使用

    复制代码
    URL.revokeObjectURL()

    释放 Blob URL,避免内存泄漏

关键代码:

复制代码
let currentAbortController = null;
let currentMediaSource = null;

// 取消上一个请求
if (currentAbortController) {
  currentAbortController.abort();
}
if (currentMediaSource?.readyState === 'open') {
  currentMediaSource.endOfStream();
}
audio.pause();
audio.src = '';

// 创建新请求
currentAbortController = new AbortController();
fetch(url, { signal: currentAbortController.signal });

Copyjavascript


4. 音频与视频动画的同步控制

**问题:**如何确保视频动画与音频完全同步(同时开始、同时停止)?

解决方案:

  • **开始同步:**在音频开始播放时启动视频循环播放

  • **结束同步:**统一封装视频停止逻辑

    复制代码
    stopVideoAnimation()

    ,在所有音频结束场景调用:

    • 音频正常播放结束(

      复制代码
      onended

      )

    • 音频播放错误(

      复制代码
      onerror

      )

    • 请求被取消(新请求到来时)

  • 避免代码重复,确保所有分支都能正确停止视频

关键代码:

复制代码
const stopVideoAnimation = () => {
  if (video) {
    video.pause();
    video.currentTime = 0
相关推荐
栉甜14 分钟前
Vue2学习内容(上)
前端·javascript·vue.js·学习
shmily麻瓜小菜鸡2 小时前
TypeScript 知识点补充(第三部分:工程化与高级类型)
前端·javascript·typescript
Hilaku3 小时前
前端的技术判断力是怎么练出来的?
前端·javascript·程序员
艾莉丝努力练剑3 小时前
【AI大模型接入SDK】ChatServer问题修复
c++·学习·ai·大模型·sdk
自养Agent3 小时前
AI 私有化实战:多模态本地化·视觉与音频怎么装下
人工智能·大模型·llm·音视频·语音识别
miofly4 小时前
GitHub 今日推荐|floorplan:AI 读代码自动画交互式架构地图
javascript·开源·github
重生之我在番茄自学网安拯救世界4 小时前
AI开发者日报 第2期 | Anthropic零 Rust 团队靠Claude重写核心 API,CPU 降 40–50%
人工智能·网络安全·大模型·ai编程·claude
Dovis(誓平步青云)4 小时前
拖动关键帧角色就抽动,动画编辑器怎样补齐中间状态
开发语言·前端·javascript·机器学习·编辑器
lpfasd1234 小时前
pywebview与Electron
前端·javascript·electron
行走的陀螺仪4 小时前
Claude Code 从入门到实战(一):安装、登录与第一个Vue项目
前端·javascript·vue.js