仅当来自两个不同元素的两个事件被触发时,如何 运行 一个函数?

How to run a function only when two events from two different elements have fired?

我有两个媒体元素:

<audio id="aud"></audio>
<video id="vid"></video>

我已将媒体 URL 附加到它们。

然后我通过执行 audio.load()video.load() 然后在 listener

中执行 audio.play()video.play() 来加载和播放它们
audio.load();
video.load();
video.addEventListener("canplay", () => {
    video.play();     
});
audio.addEventListener("canplay", () => {
    audio.play();     
});

两者都开始加载,首先加载音频,加载视频大约需要 1-2 秒。

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

我想做的是,只有当 both 触发了“canplay”事件时,才同时调用 audio.play()video.play() .我怎样才能做到这一点?

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

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

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

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

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

canplay 事件解决并结合 Promise.all 等待多个事件的承诺浮现在脑海中。

但是,它需要多复杂也取决于它的通用性。对于等待正好 one 视频和 one 音频准备播放的简单情况,我只会使用一个承诺并将事件计数为他们到了。

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

添加侦听器、设置源和return承诺的概念代码(未经测试):

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;
}

然后会被称为

 loadAV( video, video_url, audio, audio_url, 15000)
 .then(()=>{ video.play(); audio.play()})
 .catch(err=> console.log(err));

使用全局标志:

let audCanPlay=false;
let vidCanPlay=false;

function audPlay() {
  audCanPlay=true;
  console.log("audCanPlay=true");
  if(vidCanPlay) playBoth();
}

function vidPlay() {
  vidCanPlay=true;
  console.log("vidCanPlay=true");
  if(audCanPlay) playBoth();
}

function playBoth() {
  console.log("audio.play();");
  console.log("video.play();");
}

/* commented for demonstration
audio.addEventListener("canplay", audPlay);
video.addEventListener("canplay", vidPlay);
audio.load();
video.load();
*/
<audio id="aud"></audio>
<button id="audBtn" onclick="audPlay()">Mimic Audio canplay</button>
<video id="vid"></video>
<button id="vidBtn" onclick="vidPlay()">Mimic Video canplay</button>

我认为最好在调用加载方法之前添加事件侦听器。
我认为这样做更好,而不是等待一个 finish/canplay 然后才调用第二个 = 保存 time/faster.