Using $.Deferred() with nested ajax calls in a loop

孤人 提交于 2019-11-26 15:47:55

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!