Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic AJAX promise chain with jQuery

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");
            }
        });
    }
}   
like image 509
Chris Avatar asked Mar 29 '14 20:03

Chris


People also ask

Does jQuery Ajax use promises?

jQuery have promises implemented with their AJAX methods.

Can we execute run multiple Ajax requests simultaneously in jQuery?

There is a requirement to make multiple AJAX calls parallelly to fetch the required data and each successive call depends on the data fetched in its prior call. Since AJAX is asynchronous, one cannot control the order of the calls to be executed.

Does Ajax return promise?

ajax returns, which is a jqXHR object that conforms to the promise interface. If there is a failure, the outer fail function is invoked. The outer fail function is also invoked if the processData function fails. When both the getData and processData functions are successful, the outer done method is invoked.


2 Answers

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.

like image 160
Florian Margaine Avatar answered Sep 27 '22 21:09

Florian Margaine


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=?";
}
like image 30
Benjamin Gruenbaum Avatar answered Sep 27 '22 21:09

Benjamin Gruenbaum