Pregunta

My AJAX calls are built inside of a for loop. They need to be in order (synchronous). How do I chain them with jQuery?

var array = ['One', 'Two', 'Three'];
var arrayLength = array.length;
for (var arrayCounter = 0; arrayCounter < arrayLength; arrayCounter++) {
    var id = array[arrayCounter];
    getData(id);

    function getData(id) {
        $.ajax({
            url: 'http://example.com/' + id,
            dataType: 'jsonp',
            success: function(d) {
                var response = d;
                console.log(d);
            },
            error: function() {
                alert("ERROR");
            }
        });
    }
}   
¿Fue útil?

Solución

The solution using for:

var array = ['One', 'Two', 'Three'];
var id = array[0];
var data = getData(id);
for (var i = 1; i < array.length; i++) {
    // Or only the last "i" will be used
    (function (i) {
        data = data.then(function() {
            return getData(array[i]);
        });
    }(i));
}

// Also, see how better the getData can be.
function getData(id) {
    return $.ajax({
        url: 'http://example.com/' + id,
        dataType: 'jsonp',
    }).done(function(d) {
        var response = d;
        console.log(d);
    }).fail(function() {
        alert('ERROR');
    });
}

By the way, if you used a proper promises library, such as bluebird, you'd use the following code:

var array = ['One', 'Two', 'Three'];
Promise.reduce(array, function(data, id) {
    return data.promise.then(function(result) {
        return { promise: getData(id), results: data.results.push(result) };
    });
}, []).then(function(data) {
    console.log(data.results); // yay!
});

function getData(id) {
    return Promise.cast($.ajax({
        url: 'http://example.com/' + id,
        dataType: 'jsonp',
    }).done(function(d) {
        var response = d;
        console.log(d);
    }).fail(function() {
        alert('ERROR');
    }));
}

As you can see, way easier to read/write.

Otros consejos

Most promise libraries have this built in, with jQuery? Not so lucky:

First, your function should return a promise:

 function getData(id) {
        return $.ajax({ // note the return
            url: 'http://example.com/'+id,
            dataType: 'jsonp',
            success: function (d) {
                console.log(d);
            },
            error: function () {
                alert("ERROR")
            }
        });
}

Now, you chain them in a loop using .then calls. Note, that .then will execute only after the previous promise has fulfilled. So they will all run in order, one after the other.

var array = ['One', 'Two', 'Three'];
var p = $.when(1); // empty promise
array.forEach(function(el){
    p = p.then(function(){
        return getData(el);;
    });
});

All functions will run one after the other. What's left? The return value. The current implementation discards the return value. We can put the return values in an array for example:

var array = ['One', 'Two', 'Three'];
var p = $.when(1); // empty promise
var results = [];
array.forEach(function(el,index){
    p = p.then(function(){
        return getData(el);
    }).then(function(data){
        results[index] = data; // save the data
    });
});
p.then(function(){
    // all promises done, results contains the return values
});

Why stop there though, let's make it nicer :) Your entire code can be shortened to

["One","Two","Three"].map(makeUrl).map($.get).reduce(function(prev,cur,idx){
    return prev.then(cur).then(function(data){ results[idx] = data; });
},$.when(1)).then(function(){
   // results contains all responses here, all requests are sync
});

function makeUrl(id){
    return "http://example.com"+id+"?callback=?";
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top