Sara Ree
Sara Ree

Reputation: 3543

The promise resolves but the code doesn't proceed

I have a wired issue with my function. you don't need to read and understand every part of the function although I've copied all the functions for you to have a look. The MovieAudio function has PlayAudio() function inside and it's where the issue exist ...

We have this line of code inside PlayAudio() function:

console.log('before sfx');
if(sfx) { await sfxPlay(sfx.before_listen); } 
console.log('after sfx');

As you see we have two console logs right before and after this line. If sfxPlay promise resolves we should see the console.log('after sfx'); but we never see this unfortunately!

I have tested the await sfxPlay(sfx.before_listen); another place outside of this code and it works correctly!

In the first sight I thought maybe sfxPlay function doesn't resolve correctly so I put console.log('sfx has been resolved'); inside sfxPlay function to check if sfxPlay function is resolved correctly, and yes it resolves correctly but still we don't proceed to console.log('after sfx')

Here is my console. note that we don't see console.log('after sfx') which we except to see of course:

enter image description here

What am I missing? it seems simple but it doesn't behave correctly?

let StopMovieAudio;

function MovieAudio(duration, stress, option = {}) {

    let movieAudioResolve;
    const speed = option.speed ? option.speed : 1;
    const sfx = option.sfx ? false : true;
    const guideIndex =  option.guideIndex ? [...Array(option.guideIndex).keys()] : []; 
    if(!stress) stress = { to: '__', from: '__', within: ['__-__'] };
    const guide = !!guideIndex.length;
    const video = document.createElement('video');
    document.body.appendChild(video);
    video.id = 'audio-clip'; 
    const clip = document.getElementById("audio-clip");
    clip.style.visibility = "hidden";
    const source = document.createElement('source');
    source.src = getBlob(clipSource);
    source.type = 'video/mp4';
    video.appendChild(source);
    video.load();
    const start = duration.start + startPlus;
    const end = duration.end + endPlus;

    PlayAudio();
    
    async function PlayAudio() {
        clip.currentTime = start;
        clip.playbackRate = speed;
        console.log('sfx', sfx);
        console.log('before sfx');
        if(sfx) { await sfxPlay(sfx.before_listen); } // sfx is true and we enter the if block
        console.log('after sfx');
        StartAudio();
        const endCheck = setInterval(endCheckInterval, 5);
        function endCheckInterval() {
            guidePhraseUpper(guideIndex[0]);
            checkStopMovie();
        }
    } // end of Play function

    function checkStopMovie() {
        if (clip.currentTime >= end) {
            clearInterval(endCheck);
            StopMovieAudio();
            guidePhrasesDown();
        }
    }

    function guidePhraseUpper(index) {
        if(!guideIndex.length) return;
        if (clip.currentTime >= portionDurations[index].start) {
            guideIndex.shift();
            guidePhrasesDown();
            guidePhraseUp(movieSentenceArrayEn[index]);
        }
    }

    function guidePhrasesDown() {
        if(!guide) return;
        const component = $(`iframe[src*='${components.chief}']`)[0];
        referenceString.split(' ').forEach((phrase, index) => component.contentWindow.guideWordDown(index));
    }

    function StartAudio() {
        console.log('start of StartAudio');
        clip.volume = 1;
        clip.play();
        stressEffectTo();
        stressEffectFrom();
        stressEffectWithin();
    } 

    StopMovieAudio = () => {
        const audioClip = document.getElementById("audio-clip");
        if(audioClip) { document.body.removeChild(audioClip); }
        if(typeof StopMovieAudio === "function") { movieAudioResolve(); }
    } 

    function stressEffectTo() {
        if(stress.to.includes('_')) { return; }
        clip.playbackRate = 0.75;
        speedUpFrom(stress.to);
    }

    function stressEffectFrom() {
        if(stress.from.includes('_')) { return; }
        clip.playbackRate = 1;
        speedDownFrom(stress.from);
    }

    function stressEffectWithin() {
        const stressWithin = stress.within.filter(w => !w.includes('_'));
        if(!stressWithin.length){ return; }
        for(let i = 0; i < stressWithin.length; i++) {
            speedDownFrom(stressWithin[i].split('-')[0]);
            speedUpFrom(stressWithin[i].split('-')[1]);
        }
    } // end of stressEffectWithin function

    function speedUpFrom(timer) {
        const endCheck = setInterval(endCheckInterval, 5);
        function endCheckInterval() {
            if (clip.currentTime >= timer) {
                clearInterval(endCheck);
                clip.playbackRate = 1;
            }
        }
    } // end of Track function

    function speedDownFrom(timer) {
        const endCheck = setInterval(endCheckInterval, 5);
        function endCheckInterval() {
            if (clip.currentTime >= timer) {
                clearInterval(endCheck);
                clip.playbackRate = 0.7;
            }
        }
    } // end of Track function

    return new Promise(resolve => { 
        movieAudioResolve = resolve;
        console.log('Promise reached');
    });

} // end of MovieAudio function

let sfxPlayResolve;

function sfxPlay(source) {
    return new Promise(resolve => { 
        sfxPlayResolve = resolve;
        const sfxAudio = new Audio(source);
        playingAudios.push(sfxAudio);
        sfxAudio.play();
        sfxAudio.onerror = function() {
            console.log("Error loading: " + this.src);
            sfxPlay(sfx.no_sources);
            resolve(); 
        }
        sfxAudio.addEventListener("ended", function() {  
            sfxAudio.currentTime = 0;          
            resolve();    
            console.log('sfx has been resolved');    
        }); 
    });
}

Update

A Codepen has been added (I have commented the if block (where the issue exist) to show you the correct behavior of the function and You need to click on somewhere to play the audio of the video):

https://codepen.io/pixy-dixy/pen/mdOVgZw

Upvotes: 2

Views: 85

Answers (2)

Hades
Hades

Reputation: 126

The user has to interact with the document, one cannot just autoplay a video. See https://developers.google.com/web/updates/2017/09/autoplay-policy-changes

For me it works just fine when the PlayAudio function is run on clicking a button, like:

HTML:

<button onclick="startTheShow()">Play</button>

JS:

// setTimeout(async () => {
//   await MovieAudio({start: '3', end: '15'}, { to: '__', from: '__', within: ['__-__'] }, 1, true); 
// }, 5000);

const startTheShow = async () => {
  await MovieAudio({start: '3', end: '15'}, { to: '__', from: '__', within: ['__-__'] }, 1, true); 
}

Upvotes: 0

katzenhut
katzenhut

Reputation: 1742

try calling the play function like this:

let flag = true;
    sfxAudio.addEventListener("canplay", function() {  
        console.log('sfx can play');
        flag && this.play();
        flag = false;
    });

I think it is a race condition, that would also explain why your code fails locally, but works in the codepen.

The docs also recommend an Event-based approach to calling play(): https://developer.mozilla.org/en-US/docs/Web/API/HTMLAudioElement/Audio

Upvotes: 1

Related Questions