Howler.js实现MP3按序循环播放及队列播放异常排查
问题分析与修复
你的代码出现播放数量超出预期的问题,核心原因是依赖Howler的sound.playing()判断播放状态存在异步竞态:
Howler的play()是异步操作,音频需要缓冲加载后才会真正开始播放。当connection.on("send")快速连续触发多次时,第一次调用playSound创建的音频实例还没进入播放状态,sound.playing()会返回false,导致后续的playSound调用直接创建新的音频实例,最终多个音频同时播放。每个实例的onend回调都会触发队列处理,造成重复播放,最终播放数量远超添加的数量。
另外,全局sound变量被频繁覆盖,旧的音频实例可能在后台加载完成后自行播放,进一步加剧了重复播放的问题。
修复后的代码
var soundQueue = []; var isPlaying = false; // 用独立变量跟踪播放状态,避免异步判断误差 connection.on("send", (data) => { playSound(data.mp3Filename); }); function playSound(soundURL) { if (!isPlaying) { isPlaying = true; const sound = new Howl({ src: [soundURL], onplay: () => { // 确保音频真正开始播放后,标记为正在播放 isPlaying = true; }, onend: () => { isPlaying = false; // 队列不为空时,播放下一个音频 if (soundQueue.length > 0) { const nextSound = soundQueue.shift(); playSound(nextSound); } }, onloaderror: () => { // 处理音频加载失败,避免队列卡住 isPlaying = false; if (soundQueue.length > 0) { const nextSound = soundQueue.shift(); playSound(nextSound); } } }); sound.play(); } else { // 当前正在播放,将音频加入队列 soundQueue.push(soundURL); } }
关键修复点
- 用独立的
isPlaying变量跟踪状态:不再依赖sound.playing(),避免异步加载导致的状态判断错误。 - 添加
onplay事件:确保音频真正开始播放后才标记为正在播放,进一步避免竞态问题。 - 添加错误处理:通过
onloaderror处理音频加载失败的情况,防止队列卡住。 - 局部化
sound变量:不再使用全局sound变量,避免旧实例的干扰。
扩展:支持单个音频循环
如果需要单个音频循环播放,同时保留队列功能,可以修改代码,让playSound接受循环参数,并在onend回调中判断是否循环:
var soundQueue = []; var isPlaying = false; connection.on("send", (data) => { // 第二个参数为true表示循环播放该音频 playSound(data.mp3Filename, data.loop || false); }); function playSound(soundURL, loop = false) { if (!isPlaying) { isPlaying = true; const sound = new Howl({ src: [soundURL], loop: loop, onplay: () => { isPlaying = true; }, onend: () => { if (sound.loop()) { // 如果设置了循环,重新播放当前音频 sound.play(); } else { isPlaying = false; if (soundQueue.length > 0) { const nextItem = soundQueue.shift(); playSound(nextItem.url, nextItem.loop); } } }, onloaderror: () => { isPlaying = false; if (soundQueue.length > 0) { const nextItem = soundQueue.shift(); playSound(nextItem.url, nextItem.loop); } } }); sound.play(); } else { // 队列中保存包含URL和循环设置的对象 soundQueue.push({url: soundURL, loop: loop}); } }
内容的提问来源于stack exchange,提问作者Ersel Genç
相关产品推荐
相关产品推荐

