Sending one AJAX request at a time from a loop

2019-01-12 00:39发布

问题:

I know this question has been asked countless times, but I cant figure out for the life of me how to make this answer work in my case: wait for async javascript function to return

I'm looping through some "tv channels" in the outerloop and then looping through dates in the week in the innerloop. In the inner loop I make a ajax request to a server to fetch the data and I then store/cache it for later use like so

var dates = []; //<-- Contains a list of dates for the coming week 
var baseUrl = "http://www.someserver.com";
var storedChannels = [1,2,3,4,5,6,7,8,9,10,45,23,56,34,23,67,23,567,234,67,345,465,67,34];

for(ch = 0; ch < storedChannels.length; ch++) {   
    var channel = storedChannels[ch];
    for(d=0; d < 7; d++) {
        var currentDate = dates[d];
        ajax({    
            url: baseUrl+"?ch="+channel+"&dt=currentDate"+,
            complete: function(res) {
                CMLocalStore.setString('ch' + ch + "_" + scheduleDay, res);
            },
        });
        //Want to wait here till the ajax request completes.
        //Do not want to continue to next iteration.
        //Do not want to fire of 50 bazillion ajax requests all at once
        //Why? Very limited bandwidth scenario, plenty of channels  
    }
}

PS: NO JQuery please! Plain JS solutions only

Many thanks!

回答1:

You want something like this. I haven't tested it, but hopefully you should get the idea.

var dates = []; //<-- Contains a list of dates for the coming week 
var baseUrl = "http://www.someserver.com";
var storedChannels = [1,2,3,4,5,6,7,8,9,10,45,23,56,34,23,67,23,567,234,67,345,465,67,34];

function ProcessNext(ch, d) {
    if (d < 7) {
        d++;
    } else {
        d=0;
        if (ch < storedChannels.length) {
            ch++;
        } else {
            return;
        }
    }

    var channel = storedChannels[ch];
    var currentDate = dates[d];
    ajax({    
        url: baseUrl+"?ch="+channel+"&dt=currentDate"+,
        complete: function(res) {
            CMLocalStore.setString('ch' + ch + "_" + scheduleDay, res);
            ProcessNext(ch, d);
            },
    });
}

ProcessNext(0, 0);


回答2:

You need to turn your loop into a chain of callbacks.

Instead of using a loop, you should make your callback call your original function, but with a higher parameter value.



回答3:

What you are trying to do is explained in the Asynchronous Iteration Patterns tutorial by Pedro Teixeira. The examples are using Node.js but you can use the same patterns in the browser. Basically what you need to do is convert your loops to serial callbacks waiting on each other to complete, so the next AJAX request is fired from the success callback of the previous one etc. It can be done without blocking the browser but not in loops. See that tutorial.



回答4:

Essentially the answer lies in using recursive calls instead of using loops. Just wanted to add this answer for anyone that might be interested in "for loop nestings" deeper than 2 levels. As you can see its easy to extend to as many "nestings" as you like. Original credit goes to VatooVatoo implementation in Java on the DaniWeb forums.

Heres the code, tested and works (without the ajax bits of course but you can add that yourself):

<html>
<head>
<script type="text/javascript">
    function loopRecurse(a, b, c)
    {
        if(c >= 2) {
            b++;
            c=0;
            loopRecurse(a, b, c);
            return;
        }
        if(b >= 2) {
            a++;
            b=0;
            loopRecurse(a, b, c);
            return;
        }
        if(a >= 2) return;
        document.write("<div>" + a + "|" + b + "|" + c + "</div>");
        c++;
        loopRecurse(a, b, c);
    }
    loopRecurse(0, 0, 0);
</script>
</head>
<body>
    <!-- output
        0|0|0
        0|0|1
        0|1|0
        0|1|1
        1|0|0
        1|0|1
        1|1|0
        1|1|1
     -->
</body>
</html>


回答5:

See the XMLHttpRequest documentation (linked to MDC, but shouldn't matter). Basically the condition you're looking for is request.readyState==4 - presuming that you have your ajax() return the actual XMLHttpRequest object.