Javascript 如何仅在来自两个不同元素的两个事件触发时运行函数?

Javascript 如何仅在来自两个不同元素的两个事件触发时运行函数?,javascript,synchronization,html5-video,html5-audio,Javascript,Synchronization,Html5 Video,Html5 Audio,我有两个媒体元素: <audio id="aud"></audio> <video id="vid"></video> 两者都开始加载,首先加载音频,加载视频大约需要1-2秒 它们实际开始播放的时间取决于每个媒体元素何时触发“canplay”事件。(在加载至少两帧后触发) 我想做的是,仅当为两者触发了“canplay”事件时,才一起调用audio.play()和video.play()。我怎样才能做到这一点

我有两个媒体元素:

<audio id="aud"></audio>
<video id="vid"></video>
两者都开始加载,首先加载音频,加载视频大约需要1-2秒

它们实际开始播放的时间取决于每个媒体元素何时触发“canplay”事件。(在加载至少两帧后触发)

我想做的是,仅当为两者触发了“
canplay
”事件时,才一起调用
audio.play()
video.play()
。我怎样才能做到这一点

请注意,我确实需要同时使用音频和视频元素,并且我正试图在开始时实现接近但并非完美的同步

简言之:我想要实现的是:

audio.load();
video.load();

// when video can "canplay" and when audio can "canplay" ONLY then

video.play();   
audio.play();

解析的承诺可以播放
事件,并与
承诺相结合。所有
等待多个事件浮现在脑海中

然而,它需要有多复杂也取决于它的通用性。对于只等待一个视频和一个音频准备播放的简单情况,我只会使用一个承诺,并在事件到达时统计事件

请注意,要捕获检索文件的元素上的事件,在添加事件侦听器之前设置
src
href
属性是不安全的

概念代码(未测试),用于添加侦听器、设置源代码并在两者就绪时返回承诺:

const loadAV = (video, vidSrc, audio, audioSrc, timeout) {
    let resolve, reject;
    const p = new Promise( (r,j) => { resolve = r, reject = j};
    let count = 0;
    let timer;

    const listenCanPlay = () => {
        if( ++count == 2) {
            resolve();
            unListen();
        }
    };
    const unListen = () => {
        video.removeEventListener("canplay", listenCanPlay);
        audio.removeEventListener("canplay", listenCanPlay);
        if( timeout) {
            clearTimeout(timer);
        }
    };

    if( timeout) {
        timer = setTimeout( ()=> {
            if( count < 2) {
                unListen();
                reject( new Error(`loadAV timed out after ${timeout} msec`)
            }
        }, timeout);
    }
    video.addEventListener("canplay", listenCanPlay);
    audio.addEventListener("canplay", listenCanPlay);
    video.src = vidSrc;
    audio.src = audioSrc;
    return p;
}

解析的承诺可以播放
事件,并与
承诺相结合。所有
等待多个事件浮现在脑海中

然而,它需要有多复杂也取决于它的通用性。对于只等待一个视频和一个音频准备播放的简单情况,我只会使用一个承诺,并在事件到达时统计事件

请注意,要捕获检索文件的元素上的事件,在添加事件侦听器之前设置
src
href
属性是不安全的

概念代码(未测试),用于添加侦听器、设置源代码并在两者就绪时返回承诺:

const loadAV = (video, vidSrc, audio, audioSrc, timeout) {
    let resolve, reject;
    const p = new Promise( (r,j) => { resolve = r, reject = j};
    let count = 0;
    let timer;

    const listenCanPlay = () => {
        if( ++count == 2) {
            resolve();
            unListen();
        }
    };
    const unListen = () => {
        video.removeEventListener("canplay", listenCanPlay);
        audio.removeEventListener("canplay", listenCanPlay);
        if( timeout) {
            clearTimeout(timer);
        }
    };

    if( timeout) {
        timer = setTimeout( ()=> {
            if( count < 2) {
                unListen();
                reject( new Error(`loadAV timed out after ${timeout} msec`)
            }
        }, timeout);
    }
    video.addEventListener("canplay", listenCanPlay);
    audio.addEventListener("canplay", listenCanPlay);
    video.src = vidSrc;
    audio.src = audioSrc;
    return p;
}
使用全局标志:

让audCanPlay=false;
让vidCanPlay=false;
函数audPlay(){
audCanPlay=正确;
log(“audCanPlay=true”);
如果(vidCanPlay)同时播放两个();
}
函数vidPlay(){
视频播放=真;
console.log(“vidCanPlay=true”);
如果(audCanPlay)同时播放两个();
}
函数playbeath(){
console.log(“audio.play();”;
console.log(“video.play();”;
}
/*评论演示
audio.addEventListener(“canplay”,audPlay);
video.addEventListener(“canplay”,vidPlay);
load();
video.load();
*/

模拟音频可以播放
模拟视频可以播放
使用全局标志:

让audCanPlay=false;
让vidCanPlay=false;
函数audPlay(){
audCanPlay=正确;
log(“audCanPlay=true”);
如果(vidCanPlay)同时播放两个();
}
函数vidPlay(){
视频播放=真;
console.log(“vidCanPlay=true”);
如果(audCanPlay)同时播放两个();
}
函数playbeath(){
console.log(“audio.play();”;
console.log(“video.play();”;
}
/*评论演示
audio.addEventListener(“canplay”,audPlay);
video.addEventListener(“canplay”,vidPlay);
load();
video.load();
*/

模拟音频可以播放

模拟视频可以播放
不要一个接一个地加载它们。启动audio.load();然后在加载音频时触发video.load()。加载视频后,触发播放两个视频:load();addEventListener(“canplay”,()=>{video.load();});video.addEventListener(“canplay”,()=>{audio.play();video.play();});当您希望等待许多异步操作完成时,您可以使用
Promise。all
:@AntiqTech您的答案似乎比其他答案小得多,是否有可能的缺点?或者它只是为了我的用例,似乎非常适合?我只是重新排列了你的代码。我能想到的唯一缺点是“audio.load();”可能会引发异常,或者“canplay”事件由于某种原因永远不会被触发,从而中断流。如果你想投票,我可以加上这个作为答案。不要一个接一个地加载它们。启动audio.load();然后在加载音频时触发video.load()。加载视频后,触发播放两个视频:load();addEventListener(“canplay”,()=>{video.load();});video.addEventListener(“canplay”,()=>{audio.play();video.play();});当您希望等待许多异步操作完成时,您可以使用
Promise。all
:@AntiqTech您的答案似乎比其他答案小得多,是否有可能的缺点?或者它只是为了我的用例,似乎非常适合?我只是重新排列了你的代码。我能想到的唯一缺点是“audio.load();”可能会引发异常,或者“canplay”事件由于某种原因永远不会被触发,从而中断流。如果你想投票的话,我可以加上这个作为答案。嘿,谢谢你的精彩回答,不过你能检查一下AntiqTech对我问题的评论吗?使用它的缺点是什么,它对工作来说似乎很小。@Shriyanskapo或保持简单是好的。如果按顺序加载资源在性能方面是可以接受的,那么以这种方式加载资源将花费更少的代码。处理错误的条件应该包括在内,并且允许在第一对完成后播放另一个视频和声音曲目将再次开始使问题复杂化。祝你一切顺利。嘿,谢谢你的精彩回答,不过你能看看AntiqTech对我问题的评论吗?使用它的缺点是什么,它对工作来说似乎很小。@Shriyanskapo或保持简单是好的。如果按顺序加载资源在性能方面是可以接受的,那么以这种方式加载资源将花费更少的代码。处理错误的条件应该包括在内,并且允许在第一对完成后播放另一个视频和声音曲目将再次开始使事情复杂化
 loadAV( video, video_url, audio, audio_url, 15000)
 .then(()=>{ video.play(); audio.play()})
 .catch(err=> console.log(err));