setTimeout完成后如何执行函数?

时间:2017-05-16 22:08:26

标签: javascript jquery css

在此示例中,加载页面时执行gamplay()函数,这会在if语句中触发playPattern()。 playPattern接受一个数组,并在数组中的每个元素上调用setTimeout,该元素执行该元素的playAudio。 PlayAudio将一个类添加到一个元素并播放一个音频文件。

document.addEventListener("DOMContentLoaded", function(event) { 
    function playPattern(arr){
        var patternIDs = {
            0: 'blue',
            1: 'green',
            2: 'red',
            3: 'yellow'
        }
        for (var x = 0; x < arr.length; x++) {
            var myTimer = setTimeout(playAudio, 1000*x, patternIDs[arr[x]]);        
        }
        clearTimeout(myTimer);

    }//Plays a sequence of audio depending on the array values
    function playAudio(id){
        switch (id){
            case 'blue':
                document.getElementById('idBtnBlue').classList.add('btn--blue__highlight');
                document.getElementById('sound1').play();   
                break;
            case 'red':
                document.getElementById('idBtnRed').classList.add('btn--red__highlight');
                document.getElementById('sound2').play();           
                break;
            case 'green':
                document.getElementById('idBtnGreen').classList.add('btn--green__highlight');
                document.getElementById('sound3').play();           
                break;
            case 'yellow':
                document.getElementById('idBtnYellow').classList.add('btn--yellow__highlight') ;
                document.getElementById('sound4').play();       
                break;
        }
    }//Play audio 
  function removeHighlight(){
        document.getElementById('idBtnBlue').classList.remove('btn--blue__highlight');
        document.getElementById('idBtnRed').classList.remove('btn--red__highlight');
        document.getElementById('idBtnGreen').classList.remove('btn--green__highlight');
        document.getElementById('idBtnYellow').classList.remove('btn--yellow__highlight');
    }//Remove Highlight
    function gamePlay(){
        var simonArr = [];
        var userArr = [];
        var testArr = [3, 2, 0, 1, 3];

        if(strict){
            playPattern(testArr);       

        }
        else{
            playPattern(testArr);
        }
        removeHighlight();
    }//main gameplay

  gamePlay(); //Start game play 
});

我想要做的是在播放完阵列中的所有元素后调用removeHighlight()。

我试图在playPattern中的for循环之后调用removeHighlight(),或者在gameplay()中完成if语句之后调用removeHighlight(),但后来意识到在添加高亮显示之前调用了该函数。

谢谢你的任何帮助表示赞赏。 Codepen

3 个答案:

答案 0 :(得分:2)

你可以传递额外的参数来标记最后一项,然后在playAudio中调用函数:

var myTimer = setTimeout(playAudio, 1000*x, patternIDs[val],x==arr.length-1);       

pen

或者,可能是最简单的方法 - 直接用以下方法调用函数:

setTimeout(removeHighlight, 1000*(arr.length-1));       

在playPattern()结束之前。

另外,希望你知道clearTimeout()总是会破坏你的第一个调用,除非在forEach范围内声明变量。

答案 1 :(得分:1)

创建一个promises数组,用于解析音频元素onended事件。然后使用Promise.all(array).then( /* un highlight function reference*/)清除所有承诺已解决的亮点。

const patternNames = "blue,green,red,yellow".split(",");
const patternElements = patterNames.map(name => document.getElementById("idBtn" + name[0].toUpperCase() + name.substr(1)) );
function playPattern(arr){
    function playAudio(id, time){
        return new Promise((resolve) => {  // create a promise to play audio at time 
            setTimeout(() => {
                var audioElement;
                patternElements[id].classList.add("btn--" + patternNames[id] + "__highlight");
                (audioElement = document.getElementById("sound" + (id + 1))).play();   
                audioElement.onended = resolve;
            },time);
         });
    }
    Promise.all(patternNames.map((name, i) => playAudio(i, i * 1000))).then(() => { // when all sounds play then 
                                                                                    // clear all highlights
        patternElements.forEach((name, i) => patternElements[i].classList.remove("btn--" + patternNames[i] + "__highlight"));
    })
} 
playPattern([1,2,3,0,2,1]);

答案 2 :(得分:1)

setTimeout函数是异步的,因此playPattern函数实际上会在任何playAudio延迟调用之前返回。

这意味着在任何超时回调被触发之前也将调用removeHighlight。如果你想在播放所有声音时做某事,你需要一种方法来管理你的异步函数调用,并在完成这些调用后发出回调。

我喜欢使用精彩的async库来做这种类型的东西,但你也可以用Promises实现类似的东西。有了它,您可以按如下方式重写playPattern函数:

function playPattern (arr, done) {
  var patternIDs = {
    0: 'blue',
    1: 'green',
    2: 'red',
    3: 'yellow'
  }

  var iteratee = function (item, i, cb) {
    setTimeout(function (id) {
      playAudio(id)
      cb()
    }, 1000 * i, patternIDs[arr[i]])
  }

  async.eachOf(arr, iteratee, done)
}

现在,当您调用playPattern时,您可以传入一个函数作为第二个参数,只有在所有setTimeout延迟调用完成后才会调用该函数。您可以按如下方式重写gamePlay函数:

function gamePlay () {
  var testArr = [3, 2, 0, 1, 3]

  playPattern(testArray, removeHighlight)
}