Цепочка ajax-запросов с отложенным jQuery
У меня есть веб-приложение, которое должно вызывать сервер несколько раз. До сих пор у меня была длинная вложенная цепочка обратного вызова; но я хотел бы использовать функции jQuery when
, then
и т.д. Однако после использования then
я не могу показаться, что все работает снова.
$
.when ($.get('pages/run-tool.html'))
.then (function (args)
{
// This works fine
alert(args);
$('#content').replaceWith (args);
$('#progress-bar').progressbar ({value: 0});
})
.then ($.get('pages/test.html'))
.done (function(args)
{
// This prints the same as the last call
alert (args);
});
Что я делаю неправильно? Я предполагаю, что это проблема, связанная с определением области видимости, поскольку я вижу, как выполняется второй вызов get
. Использование двух разных переменных args
не помогает, поскольку аргумент, переданный выполняемой функции, по-прежнему является первым запросом get
.
Ответы
Ответ 1
Как обновление:
С помощью современного jquery (1.8+) вам не требуется предварительное, потому что get возвращает Отложенное обещание.
Кроме того, протокол устарел. Вместо этого используйте. Просто не забудьте вернуть результат нового get, который станет обещанием, присоединенным к последующим вызовам /* done */fail.
Итак:
$.get('pages/run-tool.html')
.then (function (args) { // this will run if the above .get succeeds
// This works fine
alert(args);
$('#content').replaceWith (args);
$('#progress-bar').progressbar ({value: 0});
})
.then (function() { // this will run after the above then-handler (assuming it ran)
return $.get('pages/test.html'); // the return value creates a new Deferred object
})
.done (function(args) { // this will run after the second .get succeeds (assuming it ran)
alert (args);
});
Ответ 2
Все три обратных вызова (два с then
и один с done
) применяются к одному и тому же запросу - оригинальному вызову when
. Это связано с тем, что then
возвращает тот же объект отложенного, а не новый, чтобы вы могли добавлять несколько обработчиков событий.
Вместо этого вам нужно использовать pipe
.
$
.when ($.get('pages/run-tool.html'))
.then (function (args)
{
// This works fine
alert(args);
$('#content').replaceWith (args);
$('#progress-bar').progressbar ({value: 0});
})
.pipe (function() {
return $.get('pages/test.html'); // the return value creates a new Deferred object
})
.done (function(args)
{
alert (args);
});
Ответ 3
Вот замечательно простой и высокоэффективный плагин AJAX chaining/queue. Он будет выполнять ваши ajax-методы последовательно один за другим.
Он работает, принимая массив методов, а затем выполняет их последовательно. Он не выполнит следующий метод, ожидая ответа.
//--- ЭТО ЧАСТЬ - ВАШ КОД: -----------------------
$(document).ready(function() {
var AjaxQ = [];
AjaxQ[0] = function () { AjaxMethod1(); }
AjaxQ[1] = function () { AjaxMethod2(); }
AjaxQ[3] = function () { AjaxMethod3(); }
//Execute methods in sequence
$(document).sc_ExecuteAjaxQ({ fx: AjaxQ });
});
//--- ЭТО ЧАСТЬ - ПЛАН. AJAX -------------------
$. fn.sc_ExecuteAjaxQ = function (options) {
//? Executes a series of AJAX methods in dequence
var options = $.extend({
fx: [] //function1 () { }, function2 () { }, function3 () { }
}, options);
if (options.fx.length > 0) {
var i = 0;
$(this).unbind('ajaxComplete');
$(this).ajaxComplete(function () {
i++;
if (i < options.fx.length && (typeof options.fx[i] == "function")) { options.fx[i](); }
else { $(this).unbind('ajaxComplete'); }
});
//Execute first item in queue
if (typeof options.fx[i] == "function") { options.fx[i](); }
else { $(this).unbind('ajaxComplete'); }
}
}
Ответ 4
Ответ cdr дал, который имеет самый высокий голос на данный момент, не прав.
Когда у вас есть функции a, b, c, каждый возвращает объект $.Deferred() и связывает следующие функции:
a().then(b).then(c)
Оба b и c будут выполняться после того, как будет восстановлено обещание, возвращенное из a. Поскольку обе функции then() привязаны к обещанию a, это работает аналогично другим цепочкам Jquery, таким как:
$('#id').html("<div>hello</div>").css({display:"block"})
где вызываются функции html() и css() для объекта, возвращаемого из $('# id');
Итак, чтобы выполнить a, b, c после того, как обещание, возвращенное из предыдущей функции, будет разрешено, вам нужно сделать это:
a().then(function(){
b().then(c)
});
Здесь вызов функции c связан с обещанием, возвращаемым функцией b.
Вы можете проверить это, используя следующий код:
function a() {
var promise = $.Deferred();
setTimeout(function() {
promise.resolve();
console.log("a");
}, 1000);
return promise;
}
function b() {
console.log("running b");
var promise = $.Deferred();
setTimeout(function () {
promise.resolve();
console.log("b");
}, 500);
return promise;
}
function c() {
console.log("running c");
var promise = $.Deferred();
setTimeout(function () {
promise.resolve();
console.log("c");
}, 1500);
return promise;
}
a().then(b).then(c);
a().then(function(){
b().then(c)
});
Измените обещание в функции b() от resolve() до reject(), и вы увидите разницу.
Ответ 5
<script type="text/javascript">
var promise1 = function () {
return new
$.Deferred(function (def) {
setTimeout(function () {
console.log("1");
def.resolve();
}, 3000);
}).promise();
};
var promise2 = function () {
return new
$.Deferred(function (def) {
setTimeout(function () {
console.log("2");
def.resolve();
}, 2000);
}).promise();
};
var promise3 = function () {
return new
$.Deferred(function (def) {
setTimeout(function () {
console.log("3");
def.resolve();
}, 1000);
}).promise();
};
var firstCall = function () {
console.log("firstCall");
$.when(promise1())
.then(function () { secondCall(); });
};
var secondCall = function () {
console.log("secondCall")
$.when(promise2()).then(function () { thirdCall(); });
};
var thirdCall = function () {
console.log("thirdCall")
$.when(promise3()).then(function () { console.log("done"); });
};
$(document).ready(function () {
firstCall();
});
</script>
Ответ 6
Я думал, что оставлю это небольшое упражнение здесь для всех, кто может найти это полезным, мы создаем массив запросов, и когда они будут завершены, мы можем запустить функцию обратного вызова:
var urls = [{
url: 'url1',
data: 'foo'
}, {
url: 'url2',
data: 'foo'
}, {
url: 'url3',
data: 'foo'
}, {
url: 'url4',
data: 'foo'
}];
var requests = [];
var callback = function (result) {
console.log('done!');
};
var ajaxFunction = function () {
for (var request, i = -1; request = urls[++i];) {
requests.push($.ajax({
url: request.url,
success: function (response) {
console.log('success', response);
}
}));
}
};
// using $.when.apply() we can execute a function when all the requests
// in the array have completed
$.when.apply(new ajaxFunction(), requests).done(function (result) {
callback(result)
});
Ответ 7
Мой способ - применить функцию обратного вызова:
A(function(){
B(function(){
C()})});
где A, B можно записать в виде
function A(callback)
$.ajax{
...
success: function(result){
...
if (callback) callback();
}
}