Подтвердить что ты не робот

Использование $.Deferred() с вложенными вызовами ajax в цикле

Я потратил слишком много часов на поиск похожих вопросов и попыток решения, поэтому я надеюсь, что у кого-то есть решение.

В принципе, я хотел бы получить уведомление о завершении функции a(). Проблема в том, что функция содержит ajax-вызов и цикл, который вызывает b(), который снова содержит вызов ajax.

ОБНОВЛЕНО С FIDDLE: http://jsfiddle.net/hsyj7/1/

Так же:

// 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');
  });
}

То, что я хотел бы видеть тогда, возможно, с обоими вызовами в() вверху:

a called
b called
b called
a called
b called
b called
all completed

Вместо этого я получаю

a called
all completed
b called
b called

Или какой-либо вариант.

Я знаю, что в приведенном выше коде отсутствует функция отсрочки как в цикле, так и в b(). В некоторых вариантах, которые я пробовал, обработчик done() в main() никогда не вызывается.

Кто-нибудь знает, как это сделать?

4b9b3361

Ответ 1

Да, использование Deferred - это способ сделать это:

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: Заменяется .pipe на .done.

Ответ 2

Вы можете использовать массив, который находится в более высоком контексте, чтобы вставлять объекты Promise/Deferred. Затем вы можете использовать jQuery.when рядом с Function.prototype.apply, чтобы передать все записи в качестве аргументов.

(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

Вопрос может быть старым, но поскольку нет правильного решения, я отвечу здесь. Он правильно привязывает promises, используя .then (ранее был .pipe) для достижения запрошенного результата:

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!');
  });
}

В зависимости от того, какие данные результата должны быть доступны, где может быть изменена структура вложенности/структуры, но общее правильное упорядочение.

Ответ 4

Я считаю, что это можно устранить с помощью обратных вызовов, но скрипка действительно помогла бы мне проверить вас.

 // 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.. 
    });
 }

Простите меня, если это не сработает, но я думаю, что это правильное направление.