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
相关推荐
向上的车轮3 分钟前
TypeORM——基于 TypeScript/JavaScript 的对象关系映射(ORM)框架
javascript·typescript·typeorm
程序员小寒6 分钟前
JavaScript设计模式(一):单例模式实现与应用
javascript·单例模式·设计模式
Dxy123931021611 分钟前
JS如何把数据添加到列表中
前端·javascript·vue.js
御形封灵13 分钟前
基于canvas的路网编辑交互
开发语言·javascript·交互
m0_5027249515 分钟前
Arco design vue 阻止弹窗关闭
javascript·vue.js·arco design
蜡台16 分钟前
Uniapp 实现 二手车价格评估 功能
前端·javascript·uni-app·估值·汽车抵押·二手车评估
Yan-英杰18 分钟前
TypeScript+React 全栈生态实战:从架构选型到工程落地,告别开发踩坑
javascript·学习·typescript
海天鹰20 分钟前
JSZip库读取ePub电子书目录
javascript
比特森林探险记25 分钟前
Element Plus 实战指南
前端·javascript
FlyWIHTSKY36 分钟前
Vue3 单文件中不同的组件
前端·javascript·vue.js