главная

Теги:


Использование Deferreds (отложенных объектов) в jQuery 1.5

Использование Deferreds (отложенных объектов) в jQuery 1.5 Отложенные объекты, которые были введены в jQuery 1.5, предназначены для разделения логики задачи, которая выполнялась и её последствий. Для JavaScript ничего нового тут нет; Mochikit и Dojo давно внедрили подобную функциональность, однако для jQuery новая структура AJAX и отложенные объекты являются довольно таки существенным шагом эволюции. Благодаря отложенным объектам, можно определять множество функций обратного вызова для какого-то результата, и любой из этих вызовов может быть использован после выполнения какой-либо задачи. Это может использоваться как в асинхронных, так и в обычных целях. Отложенные объекты встроены в $.ajax() так что вам самим ничего не придётся создавать. Теперь можно писать обработчики следующего вида:

 // $.get, это ajax запрос, асинхронный по умолчанию.
var req = $.get('foo.htm')
   .success(function( response ){
      // выполнять что-то с ответом
   })
   .error(function(){
      // выполнится, если запрос не прошёл
   });
 
// это может выполниться до того как $.get() будет завершён
doSomethingAwesome();
 
// если success, выполнить ещё что-то вдобавок
req.success(function( response ){
   // выполнить ещё что-то с ответом
   // это произойдёт после того как будет получен success или сразу
   // после того, как “выстрелит” первый success
});

Мы больше не ограничены одним единственным обработчиком success, error, или complete, и вместо простых функций обработчиков можем строить последовательные вызовы функций обратного вызова.

Как показано в примере мы можем располагать обработчики AJAX запросов где угодно: выше или ниже самого запроса. С точки зрения организации кода это великолепно! Теперь дни, когда использовались громадные функции обратного вызова можно забыть.

Давайте нырнём глубже. Представьте себе такую ситуацию, когда вам необходимо вызвать функцию после того, как несколько конкурирующих AJAX запросов закончат своё выполнение. Теперь эта задача решается в два счёта благодаря $.when():

function doAjax(){
   return $.get('foo.htm');
}
 
function doMoreAjax(){
   return $.get('bar.htm');
}
 
$.when( doAjax(), doMoreAjax() )
   .then(function(){
      console.log( 'Выстрелит, когда оба ajax запроса закончили свою работу!' );
   })
   .fail(function(){
      console.log( 'Выстрелит когда один или оба запроса не пройдут.' );
   });
 

Пример на jsFiddle

Всё это возможно т.к. в новой версии jQuery AJAX возвращает объект который способен отслеживать асинхронный запрос. В дальнейшем в статье будет фигурировать как “обещание”. Отложенные объекты отслеживают результаты запроса. $.when() ждёт пока выполнятся AJAX запросы, и когда это произойдёт, $.when() будет знать что вызвать .then() или .fail() в соответствии с полученным результатом. Обратные вызовы будут действовать в том порядке в котором они были созданы.

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

На самом деле $.ajax() теперь возвращает несколько запакованный объект, к которому нет прямого доступа. Что доступно нам (программистам) так это методы then(), success(), error() и другие таких как isRejected() и isResolved(), которые предназначены для проверки состояния отложенного объекта.

Почему не вернуть весь объект целиком? Если бы это было так, то реализованные методы могли выполняться ещё до того, как AJAX запросы заканчивались. Это бы нарушило всю парадигму.

Регистрация функций обратного вызова

Кроме методов then(), success(), и fail(), работу которых я продемонстрировал в прошлом примере, нам доступны и другие методы напрямую связанны с AJAX запросом. Выбор метода зависит от того, что вам необходимо сделать.

Доступная функциональность (AJAX, $.when):

 .then( doneCallbacks, failedCallbacks )
.done( doneCallbacks )
.fail( failCallbacks )

Отложенные объекты AJAX содержать несколько дополнительных метода. Они представляют собой семантические альтернативы, названия которые совпадают с теми, к которым мы уже привыкли (для того, чтобы нам было удобно):

.success( doneCallbacks )
.error( failCallbacks )
 

Вы можете создавать целые сеты функций обработчиков, которые быдут выполняться после завершения запроса. В отличии от success и error, complete это алиас метода done отдельного отложенного объекта. Такой объект создаётся внутри $.ajax() вне зависимости от результата.

 .complete( completeCallbacks )

Таким образом следующие 3 выражения полностью эквивалентны (мне кажется вариант с success привычнее для глаза чем done, а вам как больше нравится?)

 $.get("/foo/").done( fn );
// тоже самое:
$.get("/foo/").success( fn );
// тоже самое:
$.get("/foo/", fn );

Создание собственных отложенных объектов

Мы знаем, что $.ajax и $.when входят в API, но нам ничего не мешает создать наши собственные “сборки”:

function getData(){
   return $.get('/foo/');
}
 
function showDiv(){
    var dfd = $.Deferred();
 
    $('#foo').fadeIn( 1000, dfd.resolve );
 
    return dfd.promise();
}
 
$.when( getData(), showDiv() )
    .then(function( ajaxResult ){
        console.log('Анимация и AJAX запрос - оба ЗАВЕРШЕНЫ!');
 
        // 'ajaxResult' это ответ сервера
});
 

Пример на jsFiddle

Внутри showDiv() я создаю новый отложенный объект, выполняющий анимацию и возвращающий “обещание”. Отложенный объект будет завершён после того, как FadeIn() завершит свою работу. Между тем, временем пока будет возвращено “обещание” и реализуется отложенный объект, мы создаём функцию обратного вызова, которая ожидает успешное завершение двух методов. Поэтому как только обе задачи завершатся, “выстрелит” соответствующая функция обратного вызова.

getData() возвращает объект с “обещанным методом”, что позволяет $.when() отслеживать его состояние (в том числе успешное или нет). В методе showDiv() мы делаем тоже самое только “руками”.

Отложенные отложенные объекты

Мы можем пойти ещё дальше, зарегистрировав индивидуальные обратные вызовы для getData() и showDiv(), регестрируя их “обещания” на один “главный” отложенный объект.

Если вы хотите, чтобы что-то произошло при успехе getData() и при успехе showDiv() (независимо), или при успешном выполнении обоих getData() и showDiv() просто зарегистрируйте обратный вызов для их отложенных объектов, и поместите их в $.when:

function getData(){
   return $.get('/foo/').success(function(){
      console.log('Выполнится после успеха AJAX запроса');
   });
}
 
function showDiv(){
    var dfd = $.Deferred();
 
    dfd.done(function(){
      console.log('Выполнится после завершения анимации');
    });
 
    $('#foo').fadeIn( 1000, dfd.resolve );
 
    return dfd.promise();
}
 
$.when( getData(), showDiv() )
    .then(function( ajaxResult ){
        console.log('Выполнится после УСПЕХА обоих методов!');
 
        // 'ajaxResult' это ответ сервера
    });
 

Пример на jsFiddle

Горячая цепочка

Содержание отложенного объекта может быть сформировано ещё до того, как обещание будет возвращено функцией. Вот пример из реальной жизни:

function saveContact( row ){
   var form = $.tmpl(templates["contact-form"]),
      valid = true,
      messages = [],
      dfd = $.Deferred();
 
   /*
      тут целый ряд валидаторов
      ...
   */
 
   if( !valid ){
      dfd.resolve({
         success: false,
         errors: messages
      });
   } else {
      form.ajaxSubmit({
         dataType: "json",
         success: dfd.resolve,
         error: dfd.reject
      });
   }
 
   return dfd.promise();
};
 
saveContact( row )
   .then(function(response){
      if( response.success ){
         // сохранение сработало; rejoice
      } else {
         // были обнаружены ошибки валидации
         // которые содержатся в response.errors
      }
   })
   .fail(function(err) {
      // AJAX запрос не прошёл
   });
 

Функция saveContact() в первую очередь проверяет данные формы и сохраняет результат валидации в переменную valid. Если валидация не прошла, то наш отложенный объект будет содержать другой объект с полем success и массивом ошибок. Если данные формы прошли валидацию, отложенный объект будет содержать результат выполнения AJAX запроса. Функция fail() сработает при ошибках 404, 500, и других HTTP ошибках, которые могут возникнуть при работе с AJAX.

Задачи вне поля зрения

Отложенные объекты могут быть полезны во многих задачах: в синхронных и в асинхронных. Вы можете структурировать свой код так, что в зависимости от условия метод будет возвращать как “обещаение”, так и строку, объект, или что-то другое.

Данный пример демонстрирует следующую логику: при первом клике на «launch application», AJAX запрос просит сервер сохранить и возвратить текущий timestamp. Timestamp сохраняется в data-кэше элемента после того как выполнится AJAX запрос. Но это будет происходить только при первом клике. При всех следующих кликах timestamp будет браться из data-кэша, а не с сервера.

function startTask( element ){
   var timestamp = $.data( element, 'timestamp' );
 
   if( timestamp ){
       return timestamp;
   } else {
       return $.get('/start-task/').success(function( timestamp ){
           $.data( element, 'timestamp', timestamp );
       });
   }
}
 
$('#launchApplication').bind('click', function( event ){
   event.preventDefault();
 
   $.when( startTask(this) ).done(function( timestamp ){
       $('#status').html( '

Вы начали эту задачу в: ' + timestamp + '

'); }); loadApplication(); });

Когда $.when() определяет, что первый аргумент не “обещаение”, он создает и возвращает новый отложенный объект с с новым “обещанием”. Так, что в любом случае объект будет отслеживаться.

Но тут есть небольшое “но”. У вас нет возможности сделать отложенный запуск объекта, который возвращает объект со своим методом “обещание”. Отложенные объекты определяются по наличию метода “обещание”, но jQuery сам не делает проверку на существование “обещание”. Поэтому следующий код содержит синтаксическую ошибку:

   promise: function(){
      // выполнить что-то
   }
};
 
$.when( obj ).then( fn );
 

Заключение

Отложенные объекты это новый способ написания асинхронных задач. Теперь, вместо того чтобы, ломать себе голову как уместить все обработки в одну функцию обратного вызова, вы можете создать цепочку методов, которые будут последовательно выполнены. Новая информация сложна для “переваривания”, но когда вы вникните в суть, то поймете, что писать асинхронные задачи теперь намного легче.


Источник урока: www.erichynds.com/jquery/using-deferreds-in-jquery/

Урок Создан:2011-05-13 Просмотров:5061

Добавить комментарий:


Copyright© 2009 Hosted by Zhost