Понимание jQuery.Deferred и Promise
jQuery

Понимание jQuery.Deferred и Promise

Оригинал: Understanding JQuery.Deferred and Promise, José F. Romaniello

В jQuery 1.5 было введено понятие «Deferred», о котором я расскажу в статье. Я нахожу эту концепцию очень мощной при работе с JavaScript и AJAX, и я думаю, что она изменит правила, которые мы использовали при написании асинхронного кода в js.

Способ, к которому мы привыкли при работе с асинхронным кодом — передача функций обратного вызова в качестве аргументов:

Как видите, здесь мы передаем два коллбека (success и error) в метод $.ajax, используя литерал объекта.

Это работает, но во-первых, требует некой логики внтри метода post, а во-вторых, мы не можем зарегистрировать несколько обработчиков.

Введение в $.Deferred

Объект deferred довольно легко понять, но он достаточно мощный. Он имеет два важных метода:

  • resolve
  • reject

И три важных «события», позволяющих навесить обработчики:

  • done
  • fail
  • always

В качестве базового примера можно привести такой (вы можете запустить его прямо здесь):

Если вы вызовете метод «reject», сработает обработчик fail, обработчик always сработает в любом случае.

Другая интересная ососбенность заключается в том, что, если навесить обработчик уже после вызова метода resolve — он сработает немедленно:

Метод promise()

Объект deferred имеет еще один важный метод — promise(). Этот метод возвращает объект с почти тем же интерфейсом, что и у deferred объекта, он этот новый объект имеет только методы добавления обработчиков, но не имеет методов resolve и reject.

Это полезно, если вы хотите, чтобы API предоставлял возможность подписаться на события, но не давал возможности изменить состояние deferred объекта. Пример ниже не будет работать, т.к. promise не имеет метода «resolve»:

Метод $.ajax возвращает промис, поэтому вы можете сделать так:

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

Метод pipe()

Метод pipe() очень мощный и полезный, он позволяет «спроецировать» результат промиса.

На основе предыдущего примера мы можем сделать следующее:

Здесь мы делаем проекцию результата — объекта person. Таким образом, результатом будет не объект person, а строка «Saved {firstName}».

Другой интересной особенностью метода pipe() является то, что его коллбек может возвращать другой промис. Представим, что у нас есть два метода, первый получает SSN пользователя по его ID, а второй получает адрес пользоателя по SSN:

Как вы можете видеть, здесь я добавил новый метод «getPersonAddressByID». Этот метод возвращает deferred объект, скомбинированный из двух методов. Если первый из запросов завершиться неудачно, вся цепочка завершится неудачно.

Такие цепочки имеют и другие случаи использования, например, можно отклонить deferred объект внутри коллбека pipe.

Другой интересный вариант использования pipe — рекурсивные deferred объекты. Представьте, что вы запустили какую-то асинхронную операцию на сервере, и вам необходимо делать «опрос», выполнена ли опреация, и если выполнена — сделать что-либо еще:

Комбинирование промисов и $.when

Другой полезный метод — $.when. Этот метод принимает произвольное число промисов, и возвращает один deferred объект, который:

  • принимает статус «resolved», если все промисы успешно отработали;
  • принимает статус «rejected», если хотя бы один промис отработал неуспешно.

Обработчик done получит результаты каждого промиса.

Рассмотрим пример:

Как вы можете видеть, в этом примере, метод $.when возвращает новый deferred объект и мы можем использовать два результата в обработчике done.

Обратите внимание, я изменил метод getCustomer; это потому, что промис ajax вызова содержит не только результат, но другую информацию, например, код статуса.

Также вы можете использовать вместе $.when и pipe следующим образом:

Другой интересный вариант использования $.when, когда вам нужно загрузить несколько вещей на экран, но использовать только одно сообщение о загрузке:

Есть и другие полезные методы и способы использовать deferred объекты, я настоятельно рекомендую вам прочитать доментацию jQuery.

А на сегодня это все! Я надеюсь статья была полезной.

Рассылка
Подпишитесь на рассылку и получайте дайджест новостей и статей.
Никакого спама!
Подписаться