如何实现Promise终止以跳过浏览器点唱机的当前音频?
问题分析与解决方案
你的核心问题是AbortController没有和播放音频的Promise建立关联,也没在音频播放过程中监听终止信号,同时缺少终止后停止音频播放的逻辑。以下是具体修复步骤:
1. 传递并监听AbortSignal
需要把AbortController的signal传递到jukebox和playSound函数中,让Promise能响应终止信号:
修改jukebox函数
传入signal并在循环内处理终止逻辑:
// 调用jukebox时传入signal jukebox({ signal: ac.signal }); async function jukebox({ signal } = {}) { var jbsong = ''; var maxSongs = 5; jbplays = true; for (var j = 0; j < maxSongs; j++) { // 若信号已终止,可提前退出循环(可选) if (signal?.aborted) break; jbsong = getRandomSong(); try { // 把signal传递给playSound const answer = await playSound(jbsong, signal); } catch (playerror) { // 区分终止信号和音频错误 if (playerror.name === 'AbortError') { console.log("跳过当前歌曲"); // 终止后直接进入下一首循环 continue; } console.log(j + ". NOT found: " + jbsong); console.log("PlayError: " + playerror); } } jbplays = false; stopMedia(); console.log("Jukebox Finished"); };
修改playSound函数
让Promise监听终止信号,触发时停止音频并reject:
function playSound(soundfile, signal) { audio.src = soundfile; if (jbplays) { return new Promise(function(resolve, reject) { // 原有音频事件监听 audio.onerror = () => reject(new Error("音频加载失败")); audio.onended = resolve; // 监听终止信号 if (signal) { // 若信号已提前终止,直接停止音频并reject if (signal.aborted) { audio.pause(); audio.currentTime = 0; return reject(new DOMException("播放被终止", "AbortError")); } // 添加终止监听回调 const onAbort = () => { audio.pause(); audio.currentTime = 0; reject(new DOMException("播放被终止", "AbortError")); // 移除监听避免内存泄漏 signal.removeEventListener('abort', onAbort); }; signal.addEventListener('abort', onAbort); } }) } else { // 单首播放逻辑保持不变 … } };
2. 终止逻辑说明
按下S键调用ac.abort()时,当前playSound的Promise会被reject,进入catch块后通过判断AbortError类型,执行continue让循环直接进入下一首,实现“跳过”效果。同时audio.pause()和audio.currentTime = 0会立即停止当前音频并重置播放位置,避免后台播放。
3. 额外注意
如果需要支持多次跳过,每次启动jukebox前建议创建新的AbortController实例,避免旧的终止信号干扰新的播放流程。
内容的提问来源于stack exchange,提问作者R.E. Tired
相关产品推荐
相关产品推荐

