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

React.js中如何按顺序播放socket逐次推送的多个音频文件

解决方案

核心思路

当前问题的根源是每次接收到音频流都会直接覆盖播放器的src属性,前序还没播放的音频会被直接冲掉,解决方案核心逻辑如下:

  • 新增FIFO(先进先出)队列缓存所有接收到的音频流数据,不会直接覆盖当前播放内容
  • 新增播放状态标记,避免同一时间触发多次播放逻辑
  • 监听音频播放结束/出错事件,播放完成后自动按顺序读取队列中下一个音频进行播放

完整实现代码

首先在组件内新增所需的状态引用:

// 存储待播放音频的队列
const audioQueueRef = useRef([]);
// 标记当前是否处于播放状态
const isPlayingRef = useRef(false);
// 原有播放器引用不变
const pollyPlayerRef = useRef(null);

新增队列播放控制函数:

const playNext = useCallback(async () => {
  // 队列为空时标记为未播放,终止流程
  if (audioQueueRef.current.length === 0) {
    isPlayingRef.current = false;
    return;
  }
  isPlayingRef.current = true;
  // 取出队首的音频数据
  const audioBlob = audioQueueRef.current.shift();
  const audioUrl = URL.createObjectURL(audioBlob);

  // 播放结束处理逻辑
  const handlePlayEnd = () => {
    // 释放blob资源,避免内存泄漏
    URL.revokeObjectURL(audioUrl);
    // 移除事件监听避免重复绑定
    pollyPlayerRef.current.removeEventListener('ended', handlePlayEnd);
    pollyPlayerRef.current.removeEventListener('error', handlePlayError);
    // 播放下一个音频
    playNext();
  };

  // 播放出错处理逻辑
  const handlePlayError = (err) => {
    console.error('音频播放失败', err);
    URL.revokeObjectURL(audioUrl);
    pollyPlayerRef.current.removeEventListener('ended', handlePlayEnd);
    pollyPlayerRef.current.removeEventListener('error', handlePlayError);
    // 出错时跳过当前音频,继续播放下一个,可根据业务需求调整为重试逻辑
    playNext();
  };

  // 绑定事件监听
  pollyPlayerRef.current.addEventListener('ended', handlePlayEnd);
  pollyPlayerRef.current.addEventListener('error', handlePlayError);

  try {
    pollyPlayerRef.current.src = audioUrl;
    await pollyPlayerRef.current.load();
    await pollyPlayerRef.current.play();
  } catch (err) {
    console.error('音频启动播放失败', err);
    handlePlayError(err);
  }
}, []);

修改原有的socket监听逻辑:

useEffect(() => {
  const handleNewsAudioStream = async (AudioStream) => {
    if (speech && settings.rFeeds) {
      const uInt8Array = new Uint8Array(AudioStream);
      const arrayBuffer = uInt8Array.buffer;
      const blob = new Blob([arrayBuffer]);
      // 将音频推送到队列,不直接触发播放
      audioQueueRef.current.push(blob);
      // 若当前未处于播放状态,启动播放流程
      if (!isPlayingRef.current) {
        playNext();
      }
    }
  };
  socket.on("newsAudioStream", handleNewsAudioStream);

  // 组件卸载时的清理逻辑,避免内存泄漏
  return () => {
    socket.off("newsAudioStream", handleNewsAudioStream);
    audioQueueRef.current = [];
    isPlayingRef.current = false;
    if (pollyPlayerRef.current) {
      pollyPlayerRef.current.pause();
      pollyPlayerRef.current.src = '';
    }
  };
}, [settings.rFeeds, speech, playNext]);

注意事项

  • 所有通过URL.createObjectURL创建的资源必须在使用完成后调用URL.revokeObjectURL释放,长期运行下会导致严重的内存泄漏
  • 可根据业务需求扩展队列最大长度限制、播放错误重试、队列清空、暂停恢复播放等逻辑
  • 部分浏览器对自动播放有限制,需要确保用户已经和页面有过交互,否则play()方法会抛出异常

内容的提问来源于stack exchange,提问作者χerфmiи

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 11:48:03