如何检查AudioBufferSourceNode是否已启动?解决stop调用异常
解决AudioBufferSourceNode调用stop前未实际启动的问题
核心问题
AudioScheduledSourceNode.start()是异步调度的——哪怕你同步设置了playbackStarted = true,音频节点也可能还没真正进入可播放状态,这时调用stop()就会触发"未调用start就执行stop"的异常。
可行解决方案
1. 用playing事件标记实际启动状态
AudioBufferSourceNode会在音频真正开始播放时触发playing事件,用这个事件来更新播放状态,替代原来调用start()后立即赋值的逻辑:
this.audio.decodeAudioData(buffer) .then((audioBuffer) => { bufferSource.buffer = audioBuffer; bufferSource.start(0, startTime); // 仅当音频实际开始播放时,才标记状态为已启动 bufferSource.addEventListener("playing", () => { this.playbackStarted = true; console.log("this.playbackStarted=", this.playbackStarted); }); if (onEnded) { bufferSource.addEventListener("ended", () => { onEnded(); this.playbackStarted = false; console.log("this.playbackStarted=", this.playbackStarted); }); } });
后续调用pause/stop前,先检查playbackStarted是否为true,就能避免异常。
2. 给stop操作加异常捕获
如果需要快速响应暂停请求,无法等待playing事件,可以直接在调用stop()时包裹try-catch,忽略未启动的异常:
function pauseAudio() { try { bufferSource.stop(); // 这里写计算已用时间的逻辑 } catch (e) { // 忽略未启动的异常,直接重置状态 this.playbackStarted = false; } }
3. 维护严谨的状态机
自己给音频节点维护状态流转,避免依赖单一标记:
- 初始状态:
idle - 调用
start()后:scheduled - 触发
playing事件后:playing - 触发
ended或调用stop()后:stopped
只有在playing状态下才执行stop()操作,scheduled状态下不允许调用,彻底规避异常风险。
不推荐的方案
不要用固定时长的setTimeout等待启动——不同设备的音频启动延迟差异很大,固定等待时间要么在高性能设备上浪费资源,要么在低性能设备上依然触发异常。
内容的提问来源于stack exchange,提问作者Jessicaus
相关产品推荐
相关产品推荐

