简体   繁体   中英

Javascript foreach loop go to next element on callback

for(var P in ReturnData.Playlist){
    var IsLoaded = false;
    if(PlayerName == "A"){
        LoadNavA(false,false,false, ReturnData.Playlist[P], function(){
            IsLoaded = true;
            alert();
        });
        }else{
        LoadNavB(false,false,false, ReturnData.Playlist[P],function(){
            IsLoaded = true;
        });
    }
    if(IsLoaded == true) continue;
}

This is for a music playout system. The LoadNavA and LoadNavB functions load a track to the playlist asynchronously. These elements must be in the correct order. When track 3 is loaded earlier than track 2, it first places track 3 and than track 2, but it should start loading track 3 when track 1 and 2 are already loaded.

When the callback in LoadNavA or LoadNavB is called, then the loop should go on. How?

The problem you're facing is that LoadNavA and LoadNavB invoke asynchronous operations, which can return in any order, but you're attempting to enforce some ordering. This is a problem, because your loop will have completed by the time the callbacks are actually executed, so your attempts to hold-up the processing are misguided.

What you want, instead, is a means to invoke some asynchronous operations in order. I've written a general function to accomplish this:

// receives a callback and the elements to iterate
function loop(list, fn) {
    if (!list || list.length <= 0)
        return;
    (function chain(i) {
        if (i >= list.length)
            return;
        fn(list[i], function() {
            chain(i + 1);
        });
    })(0);
} 

This function assumes you're giving it a callback that will perform some operation and that that operation will kick-off the next step in the chain once it completes. To make this concrete, here is an example based off of your code.

First, a little setup, so that we can work with your example:

var PlayerName = "B";
var ReturnData = {};
ReturnData.Playlist = ["song1", "song2", "song3"];

function LoadNavA(b1, b2, b3, el, fn) {
    // this could be any asynchronous call (e.g. Ajax)
    // using setTimeout here for illustration
    window.setTimeout(fn, 2000);
}

var LoadNavB = LoadNavA; // for brevity

Now, the actual usage of loop :

loop(ReturnData.Playlist, function(song, next) {
    if (PlayerName == "A") {
        LoadNavA(false, false, false, song, function() {
            alert("LoadNavA: " + song);
            next();
        });
    } else {
        LoadNavB(false, false, false, song, function() {
            alert("LoadNavB: " + song);
            next();
        });
    }

});

The callback given as the second argument to loop will be executed once for each element in the list. It receives as arguments an element in the playlist and a function referred to as next . This callback is free to execute any asynchronous operations, as long as those operations invoke the next function once they're done. This is the mechanism by which we advance to the next element in the playlist so that the process can be repeated for every element.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM