22 декабря 2015
11 марта 2011
8
Объект jQuery Deferred
В новой версии jQuery 1.5 было внесено много изменений, однако, основные изменения коснулись внедрения объекта Deferred() в каждый метод AJAX, а так же возможность создавать объект Deferred() почти для любых функций.
Метод Deferred() позволяет создавать очередность методов, которые выполняются в зависимости от готовности определенных функций, причем как пользовательских, так и встроенных в jQuery.
Объект Deferred() может быть использован как средство для отслеживания завершения других jQuery методов и для создания отсроченных действий, что окажется полезным для выполнения определенных действий на странице в зависимости от успешного выполнения, например, AJAX запроса или методов анимации.
Предлагаем недорого —
установка сервера терминалов от компании Adivohost.
Deferred() поддерживает множественные обработчики для AJAX событий, теперь Вы можете привязывать обработчик, например success или complete, столько, сколько понадобится. Забегая вперед скажу, что примеры использования объекта Deffered() с множественными обработчиками указаны в 4-ом примере.
К объекту Deferred() команда разработчиков jQuery добавила множество сопутствующих ему методов, с помощью которых можно определять логику поведения объекта, его отмену и отслеживание, а так же передачу дополнительных объектов функциям объекта Deferred() как параметры.
Можно сказать что данный объект, по словам разработчиков jQuery, призван служить для повышения свободы действий разработчику для работы с функциями обратного вызова, которые в данном объекте не зависят от выполнения основных обработчиков методов jQuery.
В целом объект разработан на основе проекта Promises.
Возможности объекта Deferred
Объект Deferred() может быть использован в следующих случаях:
- для создания отсроченных jQuery методов;
- для создания очереди методов jQuery;
- для передачи параметров в качестве аргументов функции обратного вызова метода done();
- для отслеживания готовности jQuery методов;
- для регулирования возможного поведения дальнейшей логики JS скрипта методами then() и fail().
При этом следует помнить:
- так как объект Deferred() используется внутри каждого AJAX метода, допустимо вызывать сразу несколько обработчиков AJAX событий (см. пример 8);
- при инициализации указывать оператор new необязательно;
- объект Deferred() может принимать состояние resolve или reject один раз;
- сопутствующие меотды, указанные ниже, могут быть использованы напрямую с объектом Deferred() либо, если объект передан в переменную, то вызывая каждый метод для данной переменной.
Все возможности Deferred() можно реализовать, используя добавленные вместе с ним, вспомогательные методы.
Вспомогательные методы объекта
- deferred.done() — метод, выполняемый при успешном завершении метода, отслеживаемого методом resolve();
- deferred.fail() — метод, выполняющийся при неуспешном завершении метода, отслеживаемого методом resolve();
- deferred.isRejected() — возвращает true, если внутри объекта были вызваны методы reject() или rejectWith(), используется внутри метода fail();
- deferred.isResolved() — возвращает true, если объект находится в состоянии resolve и означает, что внутри объекта были вызваны методы resolve() или resolveWith(), используется внутри метода done();
- deferred.rejectWith() то же что и reject(), но передаёт в метод fail() параметр доступный через this;
- deferred.resolve() — запускает отслеживание выполнения метода jQuery;
- deferred.reject() — отклоняет отслеживание выполнения метода jQuery (7 пример);
- deferred.resolveWith() — то же что и resolve(), но передаёт в метод done() параметр доступный через this (см. 8-9 пример);
- deferred.then() — метод создающий цепочку последовательных функций, далее используется во многих примерах этой статьи.
Сферы применения объекта
Объект Deferred() может применяться в методах анимации (например, для методов animate, fadeOut, fadeIn, show и т.д.), во всех AJAX методах ($.get(), $.post(), $.ajax(), $.getJSON()), при этом для методов AJAX создавать его явно нет надобности.
Deferred()предоставляет большие возможности при его использовании совмести с методом when, так же введённым в jQuery 1.5, например, теперь вы можете привязывать выполнение определенной функции к успешному выполнению нескольких ajax запросов или завершению процесса анимации, либо одновременно — и к успешному выполнению нескольких ajax запросов, и к завершению процесса анимации.
Примеры использования
Примеры инициализация
Как инициализировать объект Deferred()? Очень просто, как я указал ранее, вы можете передать объект в переменную и работать далее с переменной:
var dfd = $.Deferred();
В следующем примере мы инициируем объект, после создаём метод dfd.resolve, как функцию обратного вызова, отслеживающую момент завершения анимации, созданной методом fadeIn. Конечно, вы можете спросить, зачем усложнять себе жизнь и создавать функцию обратного вызова таким непростым образом, когда можно указать в методе fadeIn обычную функцию обратного вызова? Если мы воспользуемся простой функцией обратного вызова, то мы не сможем создать множественные обработчики (пример 2).
Пример 1
$(function() {
var dfd = $.Deferred();
dfd.done(function(){
console.log('Функция 1!')
});
$('#result').fadeIn(2000, dfd.resolve);
});
Пример инициализации объекта Deferred() с множественными обработчиками.
Пример 2
// привязываем обработчики к моменту завершения ajax запроса,
// передавая при этом jqxhr объект, созданный из запроса
var jqxhr = $.ajax({ url: 'example.php' })
.success(function() { alert('success'); })
.error(function() { alert('error'); })
.complete(function() { alert('complete'); })
.completвnction() { alert('second complete'); });
// ваш код
// устанавливаем дополнительную функцию обратного вызова
jqxhr.complete(function(){ alert('third complete'); });
Примеры объекта Deferred() в AJAX
Пример реализации объекта Deferred(), когда действие, содержащееся в методе then, будет выполнено только после успешного завершения обеих AJAX функций getData() и postData():
Пример 3
$(document).ready(function() {
function getData() {
return $.get('test.php');
}
function postData() {
return $.post('test1.php');
}
$.when(postData(), getData()).then(function(){
console.log('Ajax и анимация (fadeIn) завершены!');
});
});
В обеих функциях нет надобности создавать объект Deferred, так как методы get() и post(), как и остальные AJAX методы содержат встроенный объект Deferred(), объект был добавлен в каждый метод AJAX в jQuery 1.5.
В 3-ем примере с помощью метода when, введенного так же в jQuery 1.5, создаётся очередь функций и методов jQuery, выполняемых в зависимости от завершения двух функций — postData(), getData(), указанных в качестве параметров метода when().
Метод when обязательно должен принимать функции или методы на основе объекта Deferred(), в ином случае, очередность выполнения методов не будет соблюдена. Согласно официальной документации, если в качестве параметров метода when будет передана функция, не содержащая объект Deferred(), методы, следующие за when будут выполнены немедленно. При этом количество параметров метода when не ограничено.
Так как объект Deferred() встроен во все методы AJAX, вы можете использовать его для создания множественных обработчиков AJAX событий. В следующем примере установлены три обработчика complete() для одного объекта jqXHR.
Пример 4
// привязываем обработчики к моменту завершения AJAX запроса,
// передавая при этом jqxhr объект, созданный из запроса
var jqxhr = $.ajax({ url: "example.php" })
.success(function() { alert("success"); })
.error(function() { alert("error"); })
.complete(function() { alert("complete"); })
.complete(function() { alert("second complete"); });
// ваш код
// устанавливаем дополнительную функцию обратного вызова
jqxhr.complete(function(){ alert("third complete"); });
Вспомогательный метод done может устанавливать функцию обратного вызова и принимать в качестве её параметров значения возвращаемые функциями, переданными в качестве аргументов в метод when, например, ниже оба AJAX запроса, при успешном их выполнении передадут объекты jqXHR в функцию обратного вызова done. После этого Вы можете обращаться в методе done к объектам AJAX запросов обычными способами.
Пример 5
$(document).ready(function() {
$.when($.ajax("test.php", {}), $.ajax("test1.php", {})).done(function(a1, a2) {
var jqXHR = a1[2]; /* ["success", statusText, jqXHR] */
alert(jqXHR.statusText);
});
});
Объект Deffered и методы анимации в jQuery
В предыдущих методах не были раскрыты все возможности объекта Deferred(). В методах анимации вместе с объектом допустимо использовать сопутствующие методы — fail, done, then, resolve, resolveWith, reject, rejectWith.
В следующем примере указан способ применения объекта Deferred(), когда, вновь созданные методы jDefferedIn и jDefferedOut, являясь аргументами метода when, используются для создания отсроченного метода then. Обратите особое внимание на инициализацию объекта Deferred(), чтобы выполнение определенных методов могло быть отслежено, необходимо указывать внутри функции обратного вызова метод resolve, который в свою очередь информирует о завершении функции, через метод promise():
Пример 6
$(document).ready(function () {
jQuery.fn.jDefferedIn = function() {
var dfd = $.Deferred();
$(this).fadeIn(2000, dfd.resolve);
return dfd.promise();
};
jQuery.fn.jDefferedOut = function() {
var dfd = $.Deferred();
$(this).fadeOut( 2000, dfd.resolve );
return dfd.promise();
};
$.when($('#result').jDefferedIn(), $('#result').jDefferedOut()).then(function() {
console.log('Обе функции завершены!')
});
});
Чтобы исключить действие объекта необходимо использовать метод reject() или rejectWith(). В следующем примере метод reject() отменяет дальнейшее использование объекта Deferred(), однако, в этом случае не сработает метод then, так не все методы, указанные в методе when, будут содержать объект Deferred(), вместо него сработает метод fail.
Пример 7
$(document).ready(function() {
function showDiv() {
var dfd = $.Deferred();
dfd.done(function(){
console.log('Анимация 1 завершена');
});
$("#result").fadeIn( 2000, dfd.resolve);
return dfd.promise();
}
function hideDiv() {
var dfd = $.Deferred();
dfd.done(function() {
console.log('Здесь объект отклонён');
});
dfd.reject();
$('#result').fadeOut(1000, dfd.resolve);
return dfd.promise();
}
$.when(showDiv(), hideDiv()).then(function() {
console.log('Анимация (fadeIn и fadeOut) завершены!');
}).fail(function(){
console.log('Метод fail будет запущен только в случае не успешного выполнения одной из функций showDiv(), hideDiv().');
});
});
В случае с отклонением (reject()) будет выведено сообщение на консоль, указанное в методе fail(), если же вообще удалить объект Deferred() из функции hideDiv, то будет выполнен метод then, не дожидаясь завершения анимации в функции showDiv, вследствие того, что не все методы указанные в параметрах метода when являются объектами Deferred(). Что убедится в этом, удалите из функции hideDiv() весь объект Deferred() и сопутствующие ему методы, а скорость анимации увеличте до 5 с.
Метод fail() может принимать несколько аргументов, которыми могут быть либо единичная функция, либо массив функций. Метод fail() выполняется только в случае использования методов отмены объекта Deferred() — deferred.reject() или deferred.rejectWith().
Используя объект Deferred(), появилась возможность передавать через ключевое слово this дополнительные данные, с помощью методов resolveWith и rejectWith, ниже в метод done передаётся массив, для каждого из двух созданных объектов Deferred(), для первого объекта будет выведено сообщение «Массив с именем: Alex», для второго "Массив с именем: Pete".
Пример 8
$(document).ready(function() {
var dfd = $.Deferred();
var dfdnew = $.Deferred();
dfd.done(function(){
console.log('Массив с именем: '+this.name);
});
dfdnew.done(function(){
console.log('Массив с именем: '+this.name);
});
var data = {name: "Pete", age: 15};
var newdata = {name: "Alex", age: 15};
dfdnew.resolveWith(newdata);
dfd.resolveWith(data);
return dfd.promise();
});
В следующем примере данные будут передаваться по истечении 5 секундной анимации в метод done():
Пример 9
function showDiv(){
var dfd = $.Deferred();
dfd.done(function() {
console.log('Анимация завершена. Имя:'+ this.name);
});
var data = {name: "Pete", age: 15};
$('#foo').fadeIn( 5000, function() { dfd.resolveWith(data)} );
return dfd.promise();
}
Поэкспериментируйте с rejectWith(), но не забудьте добавить метод fail(), так как с rejectWith() метод then не сработает.
Заключение
На этом всё с основными изменениями в jQuery 1.5. О других изменениях, таких как добавление новых методов $.sub(), $.parseXML(), а так же о расширенных возможностях AJAX, я напишу в следующих статьях!
От аудитории хотелось бы узнать есть ли аналог Deferred jQuery в Mootools или Prototype, а так что бы читатели хотели узнать в дальнейших статьях о jQuery.
TuTbaker в статье 9 эффективных примерчиков, а размазывать объект Deferred нет смысла, такую статью вообще не будут читать, как показала практика, статьи с подробным анализом jQuery вызывают у читателя отвращение, поскольку сильно думать заставляют, что в большинстве случаев является атрофированным явлением.