admin管理员组

文章数量:1192948

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: '/' + id,
            dataType: 'jsonp',
            success: function(d) {
                var response = d;
                console.log(d);
            },
            error: function() {
                alert("ERROR");
            }
        });
    }
}   

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");
            }
        });
    }
}   
Share Improve this question edited Mar 29, 2014 at 20:51 Rhyous 6,6802 gold badges47 silver badges52 bronze badges asked Mar 29, 2014 at 20:09 ChrisChris 3032 silver badges9 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 15

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.

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=?";
}

本文标签: javascriptDynamic AJAX promise chain with jQueryStack Overflow