I'm still trying to figure out how to use jQuery deferred object in recursive AJAX call. I have a code like this
function request(page, items){
//building the AJAX return value for JSFiddle dummy AJAX endpoint
var ret = {
totalPage: 10,
currentPage: page,
items: []
};
for (var i = page; i < (page + 5); i++){
ret.items.push(i);
}
//calling the AJAX
$.ajax({
url: '/echo/json/',
method: 'POST',
dataType: 'json',
data: {
delay: 1,
json: JSON.stringify(ret)
},
success: function(data){
if (data.currentPage <= data.totalPage){
var filtered = data.items.filter(function(el){
return el % 2 == 1;
});
var newitems = items.concat(filtered);
console.dir(newitems);
request(data.currentPage + 1, newitems);
} else {
console.dir(items);
//resolve all item
}
}
});
}
function requestAll(){
request(1, []);
//should return a promise tha contains all items
}
Here's the JSFiddle http://jsfiddle.net/petrabarus/BHswy/
I know how to use promise in single AJAX call, but I have no idea how to use it in a recursive AJAX call. I want to call the requestAll
function in a way similar like below
var promise = requestAll();
promise.done(function(items){
console.dir(items);
});
How can I do this?
You should not use the success
parameter if you want to work with promises. Instead, you want to return
a promise, and you want to use then
to transform the results of a promise into something different, possibly even another promise.
function request(page) {
…
// return the AJAX promise
return $.ajax({
url: '/echo/json/',
method: 'POST',
dataType: 'json',
data: {
delay: 1,
json: JSON.stringify(ret)
}
});
}
function requestOddsFrom(page, items) {
return request(page).then(function(data){
if (data.currentPage > data.totalPage) {
return items;
} else {
var filtered = data.items.filter(function(el){ return el%2 == 1; });
return requestOddsFrom(data.currentPage + 1, items.concat(filtered));
}
});
}
function requestAll(){
return requestOddsFrom(1, []);
}
requestAll().then(function(items) {
console.dir(items);
});
Since you're already sequencing the Ajax operations one after the other, without totally restructing your code, you can just use one deferred that you resolve on the last Ajax call:
function request(page, items, defer){
//building the AJAX return value for JSFiddle dummy AJAX endpoint
var ret = {
totalPage: 10,
currentPage: page,
items: []
};
for (var i = page; i < (page + 5); i++){
ret.items.push(i);
}
//calling the AJAX
$.ajax({
url: '/echo/json/',
method: 'POST',
dataType: 'json',
data: {
delay: 1,
json: JSON.stringify(ret)
},
success: function(data){
if (data.currentPage <= data.totalPage){
var filtered = data.items.filter(function(el){
return el % 2 == 1;
});
var newitems = items.concat(filtered);
console.dir(newitems);
request(data.currentPage + 1, newitems, defer);
} else {
console.dir(items);
//resolve the deferred
defer.resolve(items);
}
}
});
}
function requestAll(){
var deferred = jQuery.Deferred();
request(1, [], deferred);
return deferred.promise();
}
requestAll().done(function(items) {
// all ajax calls are done
});
OK, after much new promise learning, here's a fully promise version that makes use of promise chaining (returning a promise from a .then()
handler). Concepts borrowed and learned from Benji's implementation, but this is organized a bit differently and commented for learning (it would actually be quite short without comments and without the dummy Ajax call stuff):
function requestPages(startPage, endPage) {
function request(page, items){
// building the AJAX return value for
// JSFiddle dummy AJAX endpoint
var ret = {
currentPage: page,
items: []
};
for (var i = page; i < (page + 5); i++){
ret.items.push(i);
}
// Do Ajax call, return its promise
return $.ajax({
url: '/echo/json/',
method: 'POST',
dataType: 'json',
data: {
delay: 1,
json: JSON.stringify(ret)
}
}).then(function(data) {
// mock filter here to give us just odd values
var filtered = data.items.filter(function(el){
return el % 2 == 1;
});
// add these items to the ones we have so far
items = items.concat(filtered);
// if we have more pages to go, then do the next one
if (page < endPage){
// Advance the currentPage, call function to process it and
// return a new promise that will be chained back to the
// promise that was originally returned by requestPages()
return request(page + 1, items);
} else {
// Finish our iteration and
// return the accumulated items.
// This will propagate back through
// all the other promises to the original promise
// that requestPages() returned
return(items);
}
});
}
// call the first request and return it's promise
return request(startPage, []);
}
// request pages 1 through 10 inclusive
requestPages(1, 10).done(function(items) {
// all ajax calls are done
console.log(items);
});
Working jsFiddle: http://jsfiddle.net/jfriend00/pr5z9/ (be patient, it takes 10 seconds to execute for 10 Ajax calls that each take 1 second).
One issue I noticed about this version is that because it only uses the promises created by $.ajax()
, the code cannot do a .notify()
to trigger progress notifications. I found that I wanted to trigger a progress notification on the originally returned promise as each Ajax call completed, but without creating my own Deferred, I couldn't do that because you can't do a .notify()
on a promise, only on a Deferred. I'm not sure how to solve that and keep with Benji's architecture of not creating/resolving your own deferred.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With