如何实现视频事件监听器仅触发一次,移除重复调用的Promise回调
问题描述
我用以下代码创建WebGL2纹理,现在遇到个问题:playing事件的回调会在视频每次开始时重复触发,timeupdate事件则会持续触发。虽然没严重问题,但我需要它们仅触发一次,有没有办法在第一次调用后移除这些监听器?
问题代码
async function loadVideo (src, crossOrigin) { let video = makeVideo (src, crossOrigin); return new Promise((resolve, reject) => { Promise.allSettled ([ new Promise ( (resolve, reject) => {video.addEventListener('playing', () => {resolve(1);}, true )} ), new Promise ( (resolve, reject) => {video.addEventListener('timeupdate', () => {resolve(1);}, true )} ) ]) .then((values) => { resolve(video); }); }); }
完整代码示例
function makeVideo (src, crossOrigin) { let video = document.createElement("video"); if (crossOrigin) video.crossOrigin = crossOrigin; video.src = src; video.autoplay = true; video.playsInline = true; video.muted = true; video.loop = true; video.play(); return video; } async function loadVideo (src, crossOrigin) { let video = makeVideo (src, crossOrigin); video.width = 300; return new Promise((resolve, reject) => { Promise.all ([ new Promise ( (resolve, reject) => {video.addEventListener('playing', () => {resolve(1);}, true )} ), new Promise ( (resolve, reject) => {video.addEventListener('timeupdate', () => {resolve(1);}, true )} ) ]) .then((values) => { resolve(video); }); }); } document.addEventListener('DOMContentLoaded', async () => { loadVideo ("https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4") .then(vd => {document.body.appendChild(vd)}); });
解决方案
有两种简单方式实现监听器仅触发一次:
方法1:使用once配置项
addEventListener原生支持once参数,设置为true后,监听器会在触发一次后自动移除,无需手动处理。修改后的代码如下:
async function loadVideo (src, crossOrigin) { let video = makeVideo (src, crossOrigin); return new Promise((resolve, reject) => { Promise.allSettled ([ new Promise ( (resolve) => { video.addEventListener('playing', () => resolve(1), { once: true }); }), new Promise ( (resolve) => { video.addEventListener('timeupdate', () => resolve(1), { once: true }); }) ]).then(() => resolve(video)); }); }
方法2:手动移除监听器
如果需要更灵活的控制逻辑,可以先把回调函数存为变量,触发后调用removeEventListener手动移除:
async function loadVideo (src, crossOrigin) { let video = makeVideo (src, crossOrigin); return new Promise((resolve, reject) => { const handlePlaying = () => { video.removeEventListener('playing', handlePlaying, true); resolve(1); }; const handleTimeUpdate = () => { video.removeEventListener('timeupdate', handleTimeUpdate, true); resolve(1); }; Promise.allSettled ([ new Promise ( (resolve) => { video.addEventListener('playing', handlePlaying, true); }), new Promise ( (resolve) => { video.addEventListener('timeupdate', handleTimeUpdate, true); }) ]).then(() => resolve(video)); }); }
补充说明
你的代码中使用了Promise.all/Promise.allSettled,意味着只有两个事件都触发一次后,才会resolve最终的video Promise。如果需求是只要其中一个事件触发就完成,可替换为Promise.race。
内容的提问来源于stack exchange,提问作者armagedescu
相关产品推荐
相关产品推荐

