Dynamic AJAX promise chain with jQuery
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 promisearray.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 promisevar 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=?";}