clingc clingc - 5 months ago 12
Javascript Question

Breaking out of each loop of ajax calls after handling

I keep losing track of my SO accounts!

The problem:

My tool allows users to click on each year for the past 10 years to show a set of data. Not all years have data but they will still have to option to browse through each year and potentially get a "No records" message.

On initial page load, I only want to show the most recent data year and then stop sending ajax calls for any remaining years. The goal is to prevent sending 10 ajax calls on page load when users may only care about the most recent year.

Originally, my approach was to put all years through an $.each() loop with the ajax call within, and on the first year that the ajax call for that year returns data, it will break out of the loop. So I tried something like so:

function getData(path,id,year) {
return $.ajax({
url: "includes/"+path,
data: { id: id, year: year },
dataType:"json"
});
}

$.each([ 2015,2014,2013,2012,2011 ], function( k, v ) {
getData("url.php", id, v)
.done(function() {
$('#wrapper').show();
$('button[data-year="'+v+'"]').addClass('active');
someorefunctions();
})
});


The problem is I can't seem to figure out how to break out of the each loop when getData has actual data response. If there is data in 2014, I want the loop to get to 2014 and, because it gets data back for 2014, display data as instructed and then STOP sending requests for all remaining years.

I've heard the general warnings against forcing sync, for not putting ajax in loops because of their async nature. But I also want to prevent loading tons of unneeded data.

Is this possible? I originally had all of this code using
async: false
but would really like to move away from that.

EDIT: After a night of sleep, I think what I am trying to do is impossible. Because whether the next ajax request is made depends on if the previous succeeds/returns data, I have to go through this synchronously. The
.each
sends the request but it will keep sending the next regardless if the previous had finished yet. The pain point to me is that while I want this particular loops ajax to run synchronously, it doesn't mean I want all other script to stop.

EDIT I ended up using a version of the ajax calling itself and this does solve the main issue of stopping ajax on the first successful data response.

function getFirstData(path,id,years,idx) {
return $.ajax({
url: "includes/"+path,
data: { id: id, year: years[idx] },
dataType:"json",
success: function(data) {
successfunctions();
},
error: function(xhr) {
getFirstData(path,id,years,++idx);
}
});
}

getFirstData("getDonorsByCycle.php", id , [2016,2015,2014,2013] , 0);

Answer

Have you considered making one ajax call to retrieve data for all years in one shot, then processing the data accordingly?
If that's not a possibility, you can maintain which year you're currently processing then have getData call itself for the next year only if there is no data returned. If data is returned, process your callback and exit the getData call. Something like the following should work.

var years = [2015, 2014, 2013, 2012, 2011];
function getData(path,id,idx) {
    return $.ajax({
        url: "includes/"+path,
        data: { id: id, year: years[idx] },
        dataType:"json",
        success: function(dataFromServer) {
            if (dataFromServer)
                $('#wrapper').show();
                $('button[data-year="'+years[idx]+'"]').addClass('active');
                someorefunctions();
            else
                getData(path,id,++idx);
        },
        error: function(xhr) {
            handleError(xhr);
    });
}

getData("url.php", id, 0);