问题
I've spent far too many hours searching for similar questions and trying solutions, so I hope someone has a solution.
Basically, I would like to be notified when a function a() has completed. The problem is that the function contains an ajax call and a loop that calls b(), which again contains an ajax call.
UPDATED WITH FIDDLE: http://jsfiddle.net/hsyj7/1/
Like so:
// called by main()
function a() {
return $.ajax("http://url1").pipe(function(data){
for (var i = 0; i < 2; i++) {
console.log('a called');
b();
}
});
}
// called by a()
function b() {
for (var i = 0; i < 2; i++) {
$.ajax("http://url2", function(data){
// do something
console.log('b called');
}
}
}
function main(){
$.when(a()).done(function(){
console.log('all completed');
});
}
What I would like to see then is, possibly with both calls to a() at the top:
a called
b called
b called
a called
b called
b called
all completed
Instead I get
a called
all completed
b called
b called
Or some variant thereof.
I am aware that the above code is missing defer functionality in both the loop and in b(). In some of the variants I have tried, the done() handler in main() is never called.
Any one know how to do this?
回答1:
Yeah, using Deferred is the way to do that:
function a() {
var def = $.Deferred();
$.ajax("http://url1").done(function(data){
var requests = [];
for (var i = 0; i < 2; i++) {
requests.push(b());
}
$.when.apply($, requests).then(function() { def.resolve(); });
});
return def.promise();
}
// called by a()
function b() {
var def = $.Deferred(),
requests = [];
for (var i = 0; i < 2; i++) {
requests.push($.ajax("http://url2").done(function(data){
// do something
console.log('b called');
});
}
$.when.apply($, requests).then(function() { def.resolve(); });
return def.promise();
}
function main(){
$.when(a()).done(function(){
console.log('all completed');
});
}
//EDIT: Replaced .pipe with .done.
回答2:
You could use an Array which is located in a higher context to push Promise / Deferred objects into. Then you could then use jQuery.when alongside Function.prototype.apply to pass all entries as arguments.
(function() {
var promises = [ ],
when = Function.prototype.apply.bind( jQuery.when, null );
function a() {
promises.push($.ajax("http://url1").pipe(function(data){
for (var i = 0; i < 2; i++) {
console.log('a called');
b();
}
}));
return promises;
}
function b() {
for (var i = 0; i < 2; i++) {
promises.push($.ajax("http://url2", function(data) {
// do something
console.log('b called');
}));
}
}
function main() {
promises = [ ];
when( a() ).done(function(){
console.log('all completed');
});
}
}());
回答3:
The question might be old, but since there's no correct solution yet I'll put an answer here. It properly chains the promises by using .then (previsouly been .pipe) to achieve the requested result:
function a() {
return $.ajax("http://url1").done(function(data){
console.log('a called');
}).then(function(){
return $.when(b(), b()); // no loop for simplicity
});
}
function b() {
return $.ajax("http://url2").done(function(data){
console.log('b called');
});
}
function main(){
a().done(function(){
console.log('all completed');
}, function() {
console.log('an error occured!');
});
}
Depending on which result data should be available where the nesting/structure might be changed, but the overall ordering is correct.
回答4:
I believe this can be fixed with callbacks, but a fiddle would have really helped me check for you.
// called by main()
function a(callback) {
//set this to the number of loops that is going to happen
var number = 2;
return $.ajax("http://url1", function(data){
console.log('a called');
for (var i = 0; i < number ; i++) {
b();
if(number===i){
callback();
}
}
}
}
function main(){
a(function(){
//Function or code you want to run on completion..
});
}
Forgive me if this doesn't work, but i think its the right direction.
来源:https://stackoverflow.com/questions/13951456/using-deferred-with-nested-ajax-calls-in-a-loop