JavaScript音频播放异常:切换至SUCCESS状态后音频仍持续播放
解决音频在SUCCESS状态下无法停止的问题
问题根源
- 定时器引用丢失:
audioInterval是xState函数的局部变量,每次调用函数都会重新初始化audioInterval = null,导致无法获取DANGER状态下创建的定时器引用,无法清除定时播放逻辑,音频会持续重复播放。 - 冗余无效代码:SUCCESS分支里嵌套了重复的
if (status === statesName.SUCCESS)判断,属于多余代码,不影响逻辑但增加冗余。
修复方案
1. 持久化存储定时器引用
新增全局/模块级变量存储每个状态对应的定时器,确保每次调用xState都能获取到对应状态的定时器实例:
const audioStates = { Head: false, Body: false, Distance: false, Height: false }; const audioIntervals = { Head: null, Body: null, Distance: null, Height: null }; // 新增定时器存储对象
2. 修改xState函数逻辑
替换函数内的audioInterval引用为audioIntervals[name],并移除冗余判断:
const xState = ({ name, value, threshold, timer, currentState, currentStateTimeStamp, cbCurrentStateTimeStamp, cbChange, toastMsgSuccess, toastMsgDanger, }) => { let status = statesName.SUCCESS; if (value <= threshold) { timer.reset(); status = statesName.SUCCESS; } else if (value > threshold) { timer.start({ precision: 'secondTenths' }); if ( timer.getTotalTimeValues().seconds > appContext.timer_timeUntilBadPosture ) { status = statesName.DANGER; } else { status = statesName.WARNING; } } function playAudioForState(state) { switch (state) { case 'Head': dangerAudioHead.play(); break; case 'Body': dangerAudioBody.play(); break; case 'Distance': dangerAudioDistance.play(); break; case 'Height': dangerAudioHeight.play(); break; default: break; } audioStates[name] = true; } function stopAudioForState(state) { switch (state) { case 'Head': dangerAudioHead.pause(); dangerAudioHead.currentTime = 0; break; case 'Body': dangerAudioBody.pause(); dangerAudioBody.currentTime = 0; break; case 'Distance': dangerAudioDistance.pause(); dangerAudioDistance.currentTime = 0; break; case 'Height': dangerAudioHeight.pause(); dangerAudioHeight.currentTime = 0; break; default: break; } audioStates[name] = false; } if (currentState !== status) { setTimelineData([ ...timelineData, [ name, currentState, statesColourHex[currentState], currentStateTimeStamp, nextObj.createdAt, ], ]); cbCurrentStateTimeStamp(nextObj.createdAt); if (status === statesName.SUCCESS) { stopAudioForState(name); audioStates[name] = false; showToast(toastMsgSuccess, Intent.SUCCESS); // 清除对应状态的定时器 if (audioIntervals[name] !== null) { clearInterval(audioIntervals[name]); audioIntervals[name] = null; } } else if (status === statesName.DANGER) { if (!audioIntervals[name] && !audioStates[name]) { audioStates[name] = true; playAudioForState(name); audioIntervals[name] = setInterval(() => { playAudioForState(name); }, 3000); } showToast(toastMsgDanger, Intent.DANGER); } else if (status === statesName.WARNING) { if (audioIntervals[name] !== null) { clearInterval(audioIntervals[name]); audioIntervals[name] = null; } } cbChange(status); } };
3. 额外验证点
- 切换到SUCCESS状态后,检查对应音频元素是否已暂停并重置播放位置
- 确认
audioIntervals[name]已被重置为null,定时器不再触发
内容的提问来源于stack exchange,提问作者xenturion 666
相关产品推荐
相关产品推荐

