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
相关推荐
程序猿的程8 小时前
开源一个 React 股票 K 线图组件,传个股票代码就能画图
前端·javascript
大雨还洅下9 小时前
前端JS: 虚拟dom是什么? 原理? 优缺点?
javascript
唐叔在学习9 小时前
[前端特效] 左滑显示按钮的实现介绍
前端·javascript
青青家的小灰灰10 小时前
深入理解事件循环:异步编程的基石
前端·javascript·面试
前端Hardy11 小时前
HTML&CSS&JS:打造丝滑的3D彩纸飘落特效
前端·javascript·css
前端Hardy11 小时前
HTML&CSS&JS:丝滑无卡顿的明暗主题切换
javascript·css·html
warm3snow11 小时前
Claude Code 黑客马拉松:5 个获奖项目,没有一个是"纯码农"做的
ai·大模型·llm·agent·skill·mcp
UIUV12 小时前
node:child_process spawn 模块学习笔记
javascript·后端·node.js
烛阴13 小时前
Three.js 零基础入门:手把手打造交互式 3D 几何体展示系统
javascript·webgl·three.js
颜酱14 小时前
单调栈:从模板到实战
javascript·后端·算法