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
相关推荐
To_OC7 小时前
LC 42 接雨水:暴力超时卡半天?前后缀数组一用就通了
javascript·算法·leetcode
小林ixn8 小时前
从 ??= 到 onKeyDown:一个 React 组件的“自我修养”
前端·javascript·react.js
এ慕ོ冬℘゜8 小时前
前端实战:jQuery 多条件联合搜索(标题模糊查询 + 日历时间段筛选)
前端·javascript·jquery
随心点儿9 小时前
js postMessage 实现跨域发送消息-应用示例
javascript·ecmascript·postmessage
kyriewen10 小时前
我让AI改一个bug——它偷偷动了5个我没让它碰的地方
前端·javascript·ai编程
小白巨白11 小时前
玫瑰花园管理系统:AI识病+3D可视化,一套面向中小型玫瑰种植园的数字化管理工具
css·人工智能·计算机视觉·html5
hhzz12 小时前
Tiger AI Platform平台中增加人脸识别功能
图像处理·人工智能·算法·计算机视觉·大模型
gis开发之家15 小时前
《Vue3 从入门到大神35篇》Vue3 源码详解(五):effect 依赖收集原理——track 与 trigger 是如何工作的?
javascript·typescript·前端框架·vue3·vue3源码
ApacheSeaTunnel15 小时前
Apache SeaTunnel AI CLI Benchmark:7 款大模型、100 个 ETL 任务实测,谁真正能跑起来?
大数据·ai·开源·大模型·数据集成·cli·seatunnel·技术分享·数据同步
橘子星16 小时前
我一个前端切图仔,凭什么能在浏览器里跑大模型?
前端·javascript·前端框架