如何用Async/Await等待媒体元素源更新以获取duration属性
问题:更新audio元素src后无法立即获取有效duration属性
使用原生JS、Web Audio API和Canvas开发音频可视化工具时,更新audio元素的src后,尝试立即读取duration属性返回NaN,但用setTimeout延迟50ms就能拿到有效值,希望找到更优雅的替代方案。
原代码如下:
async function chooseTrack(trackTxt) { audioElem.src = `tracks/${trackTxt}`; await audioElem.load() } async function updateAudioContext(trackTxt) { await audioCtx.suspend() .then(() => chooseTrack(trackTxt)) .then(() => { console.log(audioElem) console.log(audioElem.duration) // 这里返回NaN frameLength = 360 / ((audioElem.duration * 1000)/ 3.2) ; freqHeight = 250 / bufferLength; }) }
可行但不优雅的setTimeout方案:
setTimeout(() => { console.log(audioElem.duration) // 这里能拿到有效值 frameLength = 360 / ((audioElem.duration * 1000)/ 3.2) ; freqHeight = 250 / bufferLength; }, 50)
解决方案
问题出在await audioElem.load()这一步:load()方法只是同步触发音频的加载流程,它本身不返回Promise,所以await不会等待元数据加载完成。duration属性只有在浏览器获取到音频元数据后才会变成有效值,对应的事件是loadedmetadata。
修改chooseTrack函数,让它返回一个Promise,等待loadedmetadata事件触发后再resolve:
function chooseTrack(trackTxt) { return new Promise((resolve) => { audioElem.src = `tracks/${trackTxt}`; // 监听loadedmetadata事件,确保只触发一次 audioElem.addEventListener('loadedmetadata', resolve, { once: true }); audioElem.load(); // 可选,设置src后浏览器会自动加载,调用load()可强制重新加载 }); }
之后updateAudioContext函数可以正常使用await,此时后续代码就能拿到正确的duration:
async function updateAudioContext(trackTxt) { await audioCtx.suspend(); await chooseTrack(trackTxt); // 这里duration已就绪 console.log(audioElem.duration); frameLength = 360 / ((audioElem.duration * 1000)/ 3.2); freqHeight = 250 / bufferLength; }
补充说明
loadedmetadata事件在浏览器获取到音频的基础元数据(时长、码率等)时触发,是获取duration的最早时机。- 如果需要确保音频可以开始播放,可以监听
canplay事件;如果需要确保大部分/全部音频加载完成,监听canplaythrough事件,但仅获取duration的话loadedmetadata足够。 - 使用
{ once: true }配置事件监听器,避免同一元素多次切换音频时出现重复监听的问题。
内容的提问来源于stack exchange,提问作者gbabyblue
相关产品推荐
相关产品推荐

