2013-12-16 3 views
13

Я пытаюсь использовать предложение AngularJS/then с рекурсивной функцией. Но тогда функция не вызывается (ни один из вызовов error-, success-, notify-callback не вызван).AngularJS, обещание с рекурсивной функцией

Вот мой код:

рекурсивная функция

loadSection2 = function() { 

    var apiURL = "http://..." 

    var deferred = $q.defer(); 

    $http({ 
     method: "GET", 
     url: apiURL 
    }).success(function(result, status, headers, config) { 
     console.log(result); 
     loadCount++; 
     if(loadCount < 10) { 
      newSectionArray.push(result); 
      loadSection2(); 
     } else { 
      loadCount = 0; 
      deferred.resolve(); 
      return deferred.promise; 
     } 
    }).error(function() { 
     return deferred.reject(); 
    }); 
    deferred.notify(); 
    return deferred.promise; 
}; 

затем

loadSection2().then(function() { 
    console.log("NEW SECTIONS LOADED, start adding to document"); 
    addContent(); 
}, function() { 
    console.log("ERROR CALLBACK"); 
}, function() { 
    console.log("NOTIFY CALLBACK"); 
}).then(function() { 
    loadScrollActive = false; 
}); 

Я думаю, тогда должен получить первый уведомления обратного вызова по крайней мере. Но обратного вызова нет. Не работает ли с рекурсивной функцией?

+0

Не могли бы вы дать нам jsfiddle? – Manishearth

+1

Единственное, что я вижу, это то, что вы не можете вернуть что-то из функции обратного вызова. Таким образом, возврат отложенных.произведений в .success и .error фактически ничего не делает. Однако не причина проблемы. – Narretz

+1

Где определяется 'loadCount'? И «уведомление» не работает, как вы думаете. У меня есть открытая проблема для этого в угловом репо -> https://github.com/angular/angular.js/issues/5277 – Rifat

ответ

21

EDIT - 11/11/2015 Существует более простой способ, если вы не заботитесь о уведомит:

loadSection2 = function(){ 
    var apiURL = "http://..." 
    return $http.get(apiURL) 
     .then(function(response){ 
      loadCount++;   
      if (loadCount < 10) { 
       newSectionArray.push(response.data); 
       return loadSection2(); 
      } 
      loadCount = 0; 
     }); 

}; 

Старый ответ можно найти здесь:

Вы можете непрерывно проходить обещание на всем пути.

loadSection2 = function(deferred) { 

    if(!deferred){ 
     deferred = $q.defer(); 
    } 
    var apiURL = "http://..." 

    $http({ 
     method: "GET", 
     url: apiURL 
    }).success(function(result, status, headers, config) { 
     console.log(result); 
     loadCount++; 
     if(loadCount < 10) { 
      newSectionArray.push(result); 
      loadSection2(deferred); 
     } else { 
      loadCount = 0; 
      deferred.resolve(); 
      return deferred.promise; 
     } 
    }).error(function() { 
     return deferred.reject(); 
    }); 
    deferred.notify(); 
    return deferred.promise; 
}; 
+1

Мэтью, я думаю, вы забыли на самом деле передать 'отложенный' в ваш рекурсивный звонок, но это правильный ответ. Проблема, с которой вы сталкиваетесь, заключается в том, что вы создаете новый объект 'отложенных' каждый раз, когда вы вызываете метод, вместо того, чтобы использовать один и тот же для всего процесса. Итак, в первый раз, когда вы вызываете loadSection2, вы откладываете назад 1, но отложенная, которая получает разрешение, фактически откладывается10. Передача отложенного пути помогла бы, или вы могли бы создать переменную вне своего метода и использовать закрытие для доступа к ней. – Hylianpuffball

+0

Вы совершенно правы, я хотел, чтобы он прошел, ответ отредактировал. –

+0

@MathewBerg, пожалуйста, помогите мне понять причину «отложить отложенное обещание» из блока «else». Я подготовил q-версию этого примера, где в блоке else я не возвращаю deferred.promise; и он отлично работает. [ссылка] (https://gist.github.com/pulakb/94d9d1c96e77537c304e) –

0

Fauphi,

Рекурсия полностью жизнеспособными, но не особенно "promisy" подход.

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

function loadSection2(arr) { 
    return $http({ 
     method: "GET", 
     url: "http://..." 
    }).then(function(result, status, headers, config) { 
     console.log(result); 
     arr.push(result); 
     return arr;//make the array available to the next call to loadSection2(). 
    }, function() { 
     console.log("GET error"); 
     return $q.defer().resolve(arr).promise;//allow the chain to continue, despite the error. 
     //or I think $q's .then() allows the much simpler form ... 
     //return arr; //allow the chain to continue, despite the error. 
    }); 
}; 

var newSectionPromise = $q.defer().resolve([]).promise;//note that the deferred is resolved with an anonymous new array. 
//Now we build a .then() chain, ten long, ... 
for (var i=0; i<10; i++) { 
    newSectionPromise = newSectionPromise.then(loadSection2); 
} 
// ... and do something with the populated array when the GETs have done their thing. 
newSectionPromise().then(function(arr) { 
    console.log(arr.length + " new sections loaded, start adding to document"); 
    addContent(arr); 
}, function() { 
    console.log("ERROR CALLBACK"); 
}).then(function() { 
    loadScrollActive = false; 
}); 

непроверенных

Что newSectionArray теперь создан анонимно и передается в .then() цепь независимо от успеха/неудачи отдельных GETs, возникающих в arr в обработчике успеха финальной .Затем, где она передается addContent(). Это позволяет избежать необходимости в члене newSectionArray во внешнем пространстве.

Неправильно измененный, loadSection2 может быть анонимным, что дополнительно уменьшает количество членов, добавленных в внешний охват.

Необходимость явного уведомления не исчезает:

  • больше нет мастер отложила получать уведомления
  • console.log(result); в обработчике успеха GET обеспечивает все уведомления необходимо.
3

Я хотел сделать решение, которое не проходит «откладывается» переменную вокруг и, хотя я бы не сказал, что это лучший подход, это работает, и я узнал от него (jsfiddle).

19/Aug/14 - Обновлен код до более короткой версии, удалив создание другого обещания в f1(). Надеюсь, что ясно, как это связано с первоначальным вопросом. Если это не дайте мне знать в комментарии.

f1().then(function() { 
    console.log("done"); 
}); 

function f1(counter) { 

    if (!counter) { 
     counter = 0; 
    } 

    counter++; 
    console.log(counter); 

    return asyncFunc().then(function() { 
     if (counter < 10) { 
      return f1(counter); 
     } else { 
      return; 
     } 
    }); 

} 

function asyncFunc() { 
    var deferred = $q.defer(); 

    $timeout(function() { 
     deferred.resolve(); 
    }, 100); 

    return deferred.promise; 
} 
Смежные вопросы