Использование $.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() никогда не вызывается.
Кто-нибудь знает, как это сделать?
Ответы
Ответ 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..
});
}
Простите меня, если это не сработает, но я думаю, что это правильное направление.